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 */ 133cfg_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_tuple_set(cfg_obj_t *tupleobj, const char* name, const cfg_obj_t *obj) { 320 unsigned int i; 321 const cfg_tuplefielddef_t *fields; 322 const cfg_tuplefielddef_t *f; 323 324 REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple); 325 326 fields = tupleobj->type->of; 327 for (f = fields, i = 0; f->name != NULL; f++, i++) { 328 if (strcmp(f->name, name) == 0) { 329 DE_CONST(obj, tupleobj->value.tuple[i]); 330 return (ISC_R_SUCCESS); 331 } 332 } 333 return (ISC_R_NOTFOUND); 334} 335 336isc_result_t 337cfg_parse_special(cfg_parser_t *pctx, int special) { 338 isc_result_t result; 339 CHECK(cfg_gettoken(pctx, 0)); 340 if (pctx->token.type == isc_tokentype_special && 341 pctx->token.value.as_char == special) 342 return (ISC_R_SUCCESS); 343 344 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special); 345 return (ISC_R_UNEXPECTEDTOKEN); 346 cleanup: 347 return (result); 348} 349 350/* 351 * Parse a required semicolon. If it is not there, log 352 * an error and increment the error count but continue 353 * parsing. Since the next token is pushed back, 354 * care must be taken to make sure it is eventually 355 * consumed or an infinite loop may result. 356 */ 357static isc_result_t 358parse_semicolon(cfg_parser_t *pctx) { 359 isc_result_t result; 360 CHECK(cfg_gettoken(pctx, 0)); 361 if (pctx->token.type == isc_tokentype_special && 362 pctx->token.value.as_char == ';') 363 return (ISC_R_SUCCESS); 364 365 cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'"); 366 cfg_ungettoken(pctx); 367 cleanup: 368 return (result); 369} 370 371/* 372 * Parse EOF, logging and returning an error if not there. 373 */ 374static isc_result_t 375parse_eof(cfg_parser_t *pctx) { 376 isc_result_t result; 377 CHECK(cfg_gettoken(pctx, 0)); 378 379 if (pctx->token.type == isc_tokentype_eof) 380 return (ISC_R_SUCCESS); 381 382 cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error"); 383 return (ISC_R_UNEXPECTEDTOKEN); 384 cleanup: 385 return (result); 386} 387 388/* A list of files, used internally for pctx->files. */ 389 390static cfg_type_t cfg_type_filelist = { 391 "filelist", NULL, print_list, NULL, &cfg_rep_list, 392 &cfg_type_qstring 393}; 394 395isc_result_t 396cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) { 397 isc_result_t result; 398 cfg_parser_t *pctx; 399 isc_lexspecials_t specials; 400 401 REQUIRE(mctx != NULL); 402 REQUIRE(ret != NULL && *ret == NULL); 403 404 pctx = isc_mem_get(mctx, sizeof(*pctx)); 405 if (pctx == NULL) 406 return (ISC_R_NOMEMORY); 407 408 result = isc_refcount_init(&pctx->references, 1); 409 if (result != ISC_R_SUCCESS) { 410 isc_mem_put(mctx, pctx, sizeof(*pctx)); 411 return (result); 412 } 413 414 pctx->mctx = mctx; 415 pctx->lctx = lctx; 416 pctx->lexer = NULL; 417 pctx->seen_eof = ISC_FALSE; 418 pctx->ungotten = ISC_FALSE; 419 pctx->errors = 0; 420 pctx->warnings = 0; 421 pctx->open_files = NULL; 422 pctx->closed_files = NULL; 423 pctx->line = 0; 424 pctx->callback = NULL; 425 pctx->callbackarg = NULL; 426 pctx->token.type = isc_tokentype_unknown; 427 pctx->flags = 0; 428 429 memset(specials, 0, sizeof(specials)); 430 specials['{'] = 1; 431 specials['}'] = 1; 432 specials[';'] = 1; 433 specials['/'] = 1; 434 specials['"'] = 1; 435 specials['!'] = 1; 436 437 CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer)); 438 439 isc_lex_setspecials(pctx->lexer, specials); 440 isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C | 441 ISC_LEXCOMMENT_CPLUSPLUS | 442 ISC_LEXCOMMENT_SHELL)); 443 444 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files)); 445 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files)); 446 447 *ret = pctx; 448 return (ISC_R_SUCCESS); 449 450 cleanup: 451 if (pctx->lexer != NULL) 452 isc_lex_destroy(&pctx->lexer); 453 CLEANUP_OBJ(pctx->open_files); 454 CLEANUP_OBJ(pctx->closed_files); 455 isc_mem_put(mctx, pctx, sizeof(*pctx)); 456 return (result); 457} 458 459static isc_result_t 460parser_openfile(cfg_parser_t *pctx, const char *filename) { 461 isc_result_t result; 462 cfg_listelt_t *elt = NULL; 463 cfg_obj_t *stringobj = NULL; 464 465 result = isc_lex_openfile(pctx->lexer, filename); 466 if (result != ISC_R_SUCCESS) { 467 cfg_parser_error(pctx, 0, "open: %s: %s", 468 filename, isc_result_totext(result)); 469 goto cleanup; 470 } 471 472 CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj)); 473 CHECK(create_listelt(pctx, &elt)); 474 elt->obj = stringobj; 475 ISC_LIST_APPEND(pctx->open_files->value.list, elt, link); 476 477 return (ISC_R_SUCCESS); 478 cleanup: 479 CLEANUP_OBJ(stringobj); 480 return (result); 481} 482 483void 484cfg_parser_setcallback(cfg_parser_t *pctx, 485 cfg_parsecallback_t callback, 486 void *arg) 487{ 488 pctx->callback = callback; 489 pctx->callbackarg = arg; 490} 491 492/* 493 * Parse a configuration using a pctx where a lexer has already 494 * been set up with a source. 495 */ 496static isc_result_t 497parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 498 isc_result_t result; 499 cfg_obj_t *obj = NULL; 500 501 result = cfg_parse_obj(pctx, type, &obj); 502 503 if (pctx->errors != 0) { 504 /* Errors have been logged. */ 505 if (result == ISC_R_SUCCESS) 506 result = ISC_R_FAILURE; 507 goto cleanup; 508 } 509 510 if (result != ISC_R_SUCCESS) { 511 /* Parsing failed but no errors have been logged. */ 512 cfg_parser_error(pctx, 0, "parsing failed"); 513 goto cleanup; 514 } 515 516 CHECK(parse_eof(pctx)); 517 518 *ret = obj; 519 return (ISC_R_SUCCESS); 520 521 cleanup: 522 CLEANUP_OBJ(obj); 523 return (result); 524} 525 526isc_result_t 527cfg_parse_file(cfg_parser_t *pctx, const char *filename, 528 const cfg_type_t *type, cfg_obj_t **ret) 529{ 530 isc_result_t result; 531 532 REQUIRE(filename != NULL); 533 534 CHECK(parser_openfile(pctx, filename)); 535 CHECK(parse2(pctx, type, ret)); 536 cleanup: 537 return (result); 538} 539 540 541isc_result_t 542cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer, 543 const cfg_type_t *type, cfg_obj_t **ret) 544{ 545 isc_result_t result; 546 REQUIRE(buffer != NULL); 547 CHECK(isc_lex_openbuffer(pctx->lexer, buffer)); 548 CHECK(parse2(pctx, type, ret)); 549 cleanup: 550 return (result); 551} 552 553void 554cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) { 555 REQUIRE(src != NULL); 556 REQUIRE(dest != NULL && *dest == NULL); 557 isc_refcount_increment(&src->references, NULL); 558 *dest = src; 559} 560 561void 562cfg_parser_destroy(cfg_parser_t **pctxp) { 563 cfg_parser_t *pctx = *pctxp; 564 unsigned int refs; 565 566 isc_refcount_decrement(&pctx->references, &refs); 567 if (refs == 0) { 568 isc_lex_destroy(&pctx->lexer); 569 /* 570 * Cleaning up open_files does not 571 * close the files; that was already done 572 * by closing the lexer. 573 */ 574 CLEANUP_OBJ(pctx->open_files); 575 CLEANUP_OBJ(pctx->closed_files); 576 isc_mem_put(pctx->mctx, pctx, sizeof(*pctx)); 577 } 578 *pctxp = NULL; 579} 580 581/* 582 * void 583 */ 584isc_result_t 585cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 586 UNUSED(type); 587 return (cfg_create_obj(pctx, &cfg_type_void, ret)); 588} 589 590void 591cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) { 592 UNUSED(pctx); 593 UNUSED(obj); 594} 595 596void 597cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) { 598 UNUSED(pctx); 599 UNUSED(type); 600} 601 602isc_boolean_t 603cfg_obj_isvoid(const cfg_obj_t *obj) { 604 REQUIRE(obj != NULL); 605 return (ISC_TF(obj->type->rep == &cfg_rep_void)); 606} 607 608cfg_type_t cfg_type_void = { 609 "void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void, 610 NULL }; 611 612 613/* 614 * uint32 615 */ 616isc_result_t 617cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 618 isc_result_t result; 619 cfg_obj_t *obj = NULL; 620 UNUSED(type); 621 622 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER)); 623 if (pctx->token.type != isc_tokentype_number) { 624 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number"); 625 return (ISC_R_UNEXPECTEDTOKEN); 626 } 627 628 CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj)); 629 630 obj->value.uint32 = pctx->token.value.as_ulong; 631 *ret = obj; 632 cleanup: 633 return (result); 634} 635 636void 637cfg_print_cstr(cfg_printer_t *pctx, const char *s) { 638 cfg_print_chars(pctx, s, strlen(s)); 639} 640 641void 642cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) { 643 char buf[32]; 644 snprintf(buf, sizeof(buf), "%u", u); 645 cfg_print_cstr(pctx, buf); 646} 647 648void 649cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) { 650 cfg_print_rawuint(pctx, obj->value.uint32); 651} 652 653isc_boolean_t 654cfg_obj_isuint32(const cfg_obj_t *obj) { 655 REQUIRE(obj != NULL); 656 return (ISC_TF(obj->type->rep == &cfg_rep_uint32)); 657} 658 659isc_uint32_t 660cfg_obj_asuint32(const cfg_obj_t *obj) { 661 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32); 662 return (obj->value.uint32); 663} 664 665cfg_type_t cfg_type_uint32 = { 666 "integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal, 667 &cfg_rep_uint32, NULL 668}; 669 670 671/* 672 * uint64 673 */ 674isc_boolean_t 675cfg_obj_isuint64(const cfg_obj_t *obj) { 676 REQUIRE(obj != NULL); 677 return (ISC_TF(obj->type->rep == &cfg_rep_uint64)); 678} 679 680isc_uint64_t 681cfg_obj_asuint64(const cfg_obj_t *obj) { 682 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64); 683 return (obj->value.uint64); 684} 685 686void 687cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) { 688 char buf[32]; 689 snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u", 690 obj->value.uint64); 691 cfg_print_cstr(pctx, buf); 692} 693 694cfg_type_t cfg_type_uint64 = { 695 "64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal, 696 &cfg_rep_uint64, NULL 697}; 698 699/* 700 * qstring (quoted string), ustring (unquoted string), astring 701 * (any string) 702 */ 703 704/* Create a string object from a null-terminated C string. */ 705static isc_result_t 706create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type, 707 cfg_obj_t **ret) 708{ 709 isc_result_t result; 710 cfg_obj_t *obj = NULL; 711 int len; 712 713 CHECK(cfg_create_obj(pctx, type, &obj)); 714 len = strlen(contents); 715 obj->value.string.length = len; 716 obj->value.string.base = isc_mem_get(pctx->mctx, len + 1); 717 if (obj->value.string.base == 0) { 718 isc_mem_put(pctx->mctx, obj, sizeof(*obj)); 719 return (ISC_R_NOMEMORY); 720 } 721 memcpy(obj->value.string.base, contents, len); 722 obj->value.string.base[len] = '\0'; 723 724 *ret = obj; 725 cleanup: 726 return (result); 727} 728 729isc_result_t 730cfg_create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type, 731 cfg_obj_t **ret) 732{ 733 return create_string(pctx, contents, type, ret); 734} 735 736isc_result_t 737cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 738 isc_result_t result; 739 UNUSED(type); 740 741 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); 742 if (pctx->token.type != isc_tokentype_qstring) { 743 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string"); 744 return (ISC_R_UNEXPECTEDTOKEN); 745 } 746 return (create_string(pctx, 747 TOKEN_STRING(pctx), 748 &cfg_type_qstring, 749 ret)); 750 cleanup: 751 return (result); 752} 753 754static isc_result_t 755parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 756 isc_result_t result; 757 UNUSED(type); 758 759 CHECK(cfg_gettoken(pctx, 0)); 760 if (pctx->token.type != isc_tokentype_string) { 761 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string"); 762 return (ISC_R_UNEXPECTEDTOKEN); 763 } 764 return (create_string(pctx, 765 TOKEN_STRING(pctx), 766 &cfg_type_ustring, 767 ret)); 768 cleanup: 769 return (result); 770} 771 772isc_result_t 773cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, 774 cfg_obj_t **ret) 775{ 776 isc_result_t result; 777 UNUSED(type); 778 779 CHECK(cfg_getstringtoken(pctx)); 780 return (create_string(pctx, 781 TOKEN_STRING(pctx), 782 &cfg_type_qstring, 783 ret)); 784 cleanup: 785 return (result); 786} 787 788isc_boolean_t 789cfg_is_enum(const char *s, const char *const *enums) { 790 const char * const *p; 791 for (p = enums; *p != NULL; p++) { 792 if (strcasecmp(*p, s) == 0) 793 return (ISC_TRUE); 794 } 795 return (ISC_FALSE); 796} 797 798static isc_result_t 799check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) { 800 const char *s = obj->value.string.base; 801 if (cfg_is_enum(s, enums)) 802 return (ISC_R_SUCCESS); 803 cfg_parser_error(pctx, 0, "'%s' unexpected", s); 804 return (ISC_R_UNEXPECTEDTOKEN); 805} 806 807isc_result_t 808cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 809 isc_result_t result; 810 cfg_obj_t *obj = NULL; 811 CHECK(parse_ustring(pctx, NULL, &obj)); 812 CHECK(check_enum(pctx, obj, type->of)); 813 *ret = obj; 814 return (ISC_R_SUCCESS); 815 cleanup: 816 CLEANUP_OBJ(obj); 817 return (result); 818} 819 820void 821cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) { 822 const char * const *p; 823 cfg_print_chars(pctx, "( ", 2); 824 for (p = type->of; *p != NULL; p++) { 825 cfg_print_cstr(pctx, *p); 826 if (p[1] != NULL) 827 cfg_print_chars(pctx, " | ", 3); 828 } 829 cfg_print_chars(pctx, " )", 2); 830} 831 832void 833cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) { 834 cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length); 835} 836 837static void 838print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) { 839 cfg_print_chars(pctx, "\"", 1); 840 cfg_print_ustring(pctx, obj); 841 cfg_print_chars(pctx, "\"", 1); 842} 843 844static void 845free_string(cfg_parser_t *pctx, cfg_obj_t *obj) { 846 isc_mem_put(pctx->mctx, obj->value.string.base, 847 obj->value.string.length + 1); 848} 849 850isc_boolean_t 851cfg_obj_isstring(const cfg_obj_t *obj) { 852 REQUIRE(obj != NULL); 853 return (ISC_TF(obj->type->rep == &cfg_rep_string)); 854} 855 856const char * 857cfg_obj_asstring(const cfg_obj_t *obj) { 858 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string); 859 return (obj->value.string.base); 860} 861 862/* Quoted string only */ 863cfg_type_t cfg_type_qstring = { 864 "quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal, 865 &cfg_rep_string, NULL 866}; 867 868/* Unquoted string only */ 869cfg_type_t cfg_type_ustring = { 870 "string", parse_ustring, cfg_print_ustring, cfg_doc_terminal, 871 &cfg_rep_string, NULL 872}; 873 874/* Any string (quoted or unquoted); printed with quotes */ 875cfg_type_t cfg_type_astring = { 876 "string", cfg_parse_astring, print_qstring, cfg_doc_terminal, 877 &cfg_rep_string, NULL 878}; 879 880/* 881 * Booleans 882 */ 883 884isc_boolean_t 885cfg_obj_isboolean(const cfg_obj_t *obj) { 886 REQUIRE(obj != NULL); 887 return (ISC_TF(obj->type->rep == &cfg_rep_boolean)); 888} 889 890isc_boolean_t 891cfg_obj_asboolean(const cfg_obj_t *obj) { 892 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean); 893 return (obj->value.boolean); 894} 895 896isc_result_t 897cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) 898{ 899 isc_result_t result; 900 isc_boolean_t value; 901 cfg_obj_t *obj = NULL; 902 UNUSED(type); 903 904 result = cfg_gettoken(pctx, 0); 905 if (result != ISC_R_SUCCESS) 906 return (result); 907 908 if (pctx->token.type != isc_tokentype_string) 909 goto bad_boolean; 910 911 if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) || 912 (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) || 913 (strcmp(TOKEN_STRING(pctx), "1") == 0)) { 914 value = ISC_TRUE; 915 } else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) || 916 (strcasecmp(TOKEN_STRING(pctx), "no") == 0) || 917 (strcmp(TOKEN_STRING(pctx), "0") == 0)) { 918 value = ISC_FALSE; 919 } else { 920 goto bad_boolean; 921 } 922 923 CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj)); 924 obj->value.boolean = value; 925 *ret = obj; 926 return (result); 927 928 bad_boolean: 929 cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected"); 930 return (ISC_R_UNEXPECTEDTOKEN); 931 932 cleanup: 933 return (result); 934} 935 936void 937cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) { 938 if (obj->value.boolean) 939 cfg_print_chars(pctx, "yes", 3); 940 else 941 cfg_print_chars(pctx, "no", 2); 942} 943 944cfg_type_t cfg_type_boolean = { 945 "boolean", cfg_parse_boolean, cfg_print_boolean, cfg_doc_terminal, 946 &cfg_rep_boolean, NULL 947}; 948 949/* 950 * Lists. 951 */ 952 953isc_result_t 954cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) { 955 isc_result_t result; 956 CHECK(cfg_create_obj(pctx, type, obj)); 957 ISC_LIST_INIT((*obj)->value.list); 958 cleanup: 959 return (result); 960} 961 962static isc_result_t 963create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) { 964 cfg_listelt_t *elt; 965 elt = isc_mem_get(pctx->mctx, sizeof(*elt)); 966 if (elt == NULL) 967 return (ISC_R_NOMEMORY); 968 elt->obj = NULL; 969 ISC_LINK_INIT(elt, link); 970 *eltp = elt; 971 return (ISC_R_SUCCESS); 972} 973 974isc_result_t 975cfg_create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) 976{ 977 return create_listelt(pctx, eltp); 978} 979 980static void 981free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) { 982 if (elt->obj != NULL) 983 cfg_obj_destroy(pctx, &elt->obj); 984 isc_mem_put(pctx->mctx, elt, sizeof(*elt)); 985} 986 987void 988cfg_destroy_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) 989{ 990 free_list_elt(pctx, *eltp); 991 *eltp = NULL; 992} 993 994static void 995free_list(cfg_parser_t *pctx, cfg_obj_t *obj) { 996 cfg_listelt_t *elt, *next; 997 for (elt = ISC_LIST_HEAD(obj->value.list); 998 elt != NULL; 999 elt = next) 1000 { 1001 next = ISC_LIST_NEXT(elt, link); 1002 free_list_elt(pctx, elt); 1003 } 1004} 1005 1006isc_result_t 1007cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype, 1008 cfg_listelt_t **ret) 1009{ 1010 isc_result_t result; 1011 cfg_listelt_t *elt = NULL; 1012 cfg_obj_t *value = NULL; 1013 1014 CHECK(create_listelt(pctx, &elt)); 1015 1016 result = cfg_parse_obj(pctx, elttype, &value); 1017 if (result != ISC_R_SUCCESS) 1018 goto cleanup; 1019 1020 elt->obj = value; 1021 1022 *ret = elt; 1023 return (ISC_R_SUCCESS); 1024 1025 cleanup: 1026 isc_mem_put(pctx->mctx, elt, sizeof(*elt)); 1027 return (result); 1028} 1029 1030/* 1031 * Parse a homogeneous list whose elements are of type 'elttype' 1032 * and where each element is terminated by a semicolon. 1033 */ 1034static isc_result_t 1035parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret) 1036{ 1037 cfg_obj_t *listobj = NULL; 1038 const cfg_type_t *listof = listtype->of; 1039 isc_result_t result; 1040 cfg_listelt_t *elt = NULL; 1041 1042 CHECK(cfg_create_list(pctx, listtype, &listobj)); 1043 1044 for (;;) { 1045 CHECK(cfg_peektoken(pctx, 0)); 1046 if (pctx->token.type == isc_tokentype_special && 1047 pctx->token.value.as_char == /*{*/ '}') 1048 break; 1049 CHECK(cfg_parse_listelt(pctx, listof, &elt)); 1050 CHECK(parse_semicolon(pctx)); 1051 ISC_LIST_APPEND(listobj->value.list, elt, link); 1052 elt = NULL; 1053 } 1054 *ret = listobj; 1055 return (ISC_R_SUCCESS); 1056 1057 cleanup: 1058 if (elt != NULL) 1059 free_list_elt(pctx, elt); 1060 CLEANUP_OBJ(listobj); 1061 return (result); 1062} 1063 1064static void 1065print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1066 const cfg_list_t *list = &obj->value.list; 1067 const cfg_listelt_t *elt; 1068 1069 for (elt = ISC_LIST_HEAD(*list); 1070 elt != NULL; 1071 elt = ISC_LIST_NEXT(elt, link)) { 1072 print_indent(pctx); 1073 cfg_print_obj(pctx, elt->obj); 1074 cfg_print_chars(pctx, ";\n", 2); 1075 } 1076} 1077 1078isc_result_t 1079cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type, 1080 cfg_obj_t **ret) 1081{ 1082 isc_result_t result; 1083 CHECK(cfg_parse_special(pctx, '{')); 1084 CHECK(parse_list(pctx, type, ret)); 1085 CHECK(cfg_parse_special(pctx, '}')); 1086 cleanup: 1087 return (result); 1088} 1089 1090void 1091cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1092 print_open(pctx); 1093 print_list(pctx, obj); 1094 print_close(pctx); 1095} 1096 1097void 1098cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) { 1099 cfg_print_chars(pctx, "{ ", 2); 1100 cfg_doc_obj(pctx, type->of); 1101 cfg_print_chars(pctx, "; ... }", 7); 1102} 1103 1104/* 1105 * Parse a homogeneous list whose elements are of type 'elttype' 1106 * and where elements are separated by space. The list ends 1107 * before the first semicolon. 1108 */ 1109isc_result_t 1110cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype, 1111 cfg_obj_t **ret) 1112{ 1113 cfg_obj_t *listobj = NULL; 1114 const cfg_type_t *listof = listtype->of; 1115 isc_result_t result; 1116 1117 CHECK(cfg_create_list(pctx, listtype, &listobj)); 1118 1119 for (;;) { 1120 cfg_listelt_t *elt = NULL; 1121 1122 CHECK(cfg_peektoken(pctx, 0)); 1123 if (pctx->token.type == isc_tokentype_special && 1124 pctx->token.value.as_char == ';') 1125 break; 1126 CHECK(cfg_parse_listelt(pctx, listof, &elt)); 1127 ISC_LIST_APPEND(listobj->value.list, elt, link); 1128 } 1129 *ret = listobj; 1130 return (ISC_R_SUCCESS); 1131 1132 cleanup: 1133 CLEANUP_OBJ(listobj); 1134 return (result); 1135} 1136 1137void 1138cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1139 const cfg_list_t *list = &obj->value.list; 1140 const cfg_listelt_t *elt; 1141 1142 for (elt = ISC_LIST_HEAD(*list); 1143 elt != NULL; 1144 elt = ISC_LIST_NEXT(elt, link)) { 1145 cfg_print_obj(pctx, elt->obj); 1146 if (ISC_LIST_NEXT(elt, link) != NULL) 1147 cfg_print_chars(pctx, " ", 1); 1148 } 1149} 1150 1151isc_boolean_t 1152cfg_obj_islist(const cfg_obj_t *obj) { 1153 REQUIRE(obj != NULL); 1154 return (ISC_TF(obj->type->rep == &cfg_rep_list)); 1155} 1156 1157const cfg_listelt_t * 1158cfg_list_first(const cfg_obj_t *obj) { 1159 REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list); 1160 if (obj == NULL) 1161 return (NULL); 1162 return (ISC_LIST_HEAD(obj->value.list)); 1163} 1164 1165const cfg_listelt_t * 1166cfg_list_next(const cfg_listelt_t *elt) { 1167 REQUIRE(elt != NULL); 1168 return (ISC_LIST_NEXT(elt, link)); 1169} 1170 1171/* 1172 * Return the length of a list object. If obj is NULL or is not 1173 * a list, return 0. 1174 */ 1175unsigned int 1176cfg_list_length(const cfg_obj_t *obj, isc_boolean_t recurse) { 1177 const cfg_listelt_t *elt; 1178 unsigned int count = 0; 1179 1180 if (obj == NULL || !cfg_obj_islist(obj)) 1181 return (0U); 1182 for (elt = cfg_list_first(obj); 1183 elt != NULL; 1184 elt = cfg_list_next(elt)) { 1185 if (recurse && cfg_obj_islist(elt->obj)) { 1186 count += cfg_list_length(elt->obj, recurse); 1187 } else { 1188 count++; 1189 } 1190 } 1191 return (count); 1192} 1193 1194cfg_obj_t * 1195cfg_listelt_value(const cfg_listelt_t *elt) { 1196 REQUIRE(elt != NULL); 1197 return (elt->obj); 1198} 1199 1200void 1201cfg_listelt_setvalue(cfg_listelt_t *elt, const cfg_obj_t *obj) { 1202 REQUIRE(elt != NULL); 1203 REQUIRE(obj != NULL); 1204 DE_CONST(obj, elt->obj); 1205} 1206 1207/* 1208 * Maps. 1209 */ 1210 1211isc_result_t 1212cfg_create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1213 isc_result_t result; 1214 cfg_obj_t* mapobj; 1215 1216 REQUIRE(ret != NULL && *ret == NULL); 1217 1218 result = create_map(pctx, type, &mapobj); 1219 if (result == ISC_R_SUCCESS) { 1220 const cfg_clausedef_t * const *clausesets = type->of; 1221 mapobj->value.map.clausesets = clausesets; 1222 1223 *ret = mapobj; 1224 } 1225 1226 return (result); 1227} 1228 1229isc_result_t 1230cfg_map_set(cfg_obj_t *mapobj, const char* name, const cfg_obj_t *obj) { 1231 isc_result_t result; 1232 isc_symvalue_t val; 1233 const cfg_map_t *map; 1234 1235 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 1236 REQUIRE(name != NULL); 1237 1238 map = &mapobj->value.map; 1239 1240 DE_CONST(obj, val.as_pointer); 1241 result = isc_symtab_define(map->symtab, name, 1, val, isc_symexists_replace); 1242 return (result); 1243} 1244 1245isc_result_t 1246cfg_map_delete(cfg_obj_t *mapobj, const char* name) { 1247 isc_result_t result; 1248 const cfg_map_t *map; 1249 1250 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 1251 REQUIRE(name != NULL); 1252 1253 map = &mapobj->value.map; 1254 1255 result = isc_symtab_undefine(map->symtab, name, 1); 1256 return (result); 1257} 1258 1259isc_result_t 1260cfg_map_move(cfg_obj_t *mapobjdst, cfg_obj_t *mapobjsrc, const char* name) { 1261 isc_result_t result; 1262 isc_symvalue_t val; 1263 const cfg_map_t *mapsrc, *mapdst; 1264 isc_symtabaction_t saved_undef_action = NULL; 1265 void* saved_undef_arg = NULL; 1266 1267 REQUIRE(mapobjsrc != NULL && mapobjsrc->type->rep == &cfg_rep_map); 1268 REQUIRE(mapobjdst != NULL && mapobjdst->type->rep == &cfg_rep_map); 1269 REQUIRE(name != NULL); 1270 1271 mapsrc = &mapobjsrc->value.map; 1272 mapdst = &mapobjdst->value.map; 1273 1274 result = isc_symtab_lookup(mapsrc->symtab, name, MAP_SYM, &val); 1275 if (result == ISC_R_SUCCESS) { 1276 /* Don't want the object to get destroyed when it is removed from 1277 * the old map. So temporarily set the undefine_action to NULL which 1278 * will cause the map to forget about the object without actually 1279 * destroying the object. 1280 */ 1281 isc_symtab_getundefineaction(mapsrc->symtab, &saved_undef_action, &saved_undef_arg); 1282 isc_symtab_setundefineaction(mapsrc->symtab, NULL, NULL); 1283 isc_symtab_undefine(mapsrc->symtab, name, 1); 1284 isc_symtab_setundefineaction(mapsrc->symtab, saved_undef_action, saved_undef_arg); 1285 1286 result = isc_symtab_define(mapdst->symtab, name, 1, val, isc_symexists_replace); 1287 } 1288 return result; 1289} 1290 1291isc_result_t 1292cfg_map_moveall(cfg_obj_t *mapobjdst, cfg_obj_t *mapobjsrc) { 1293 isc_result_t result; 1294 const cfg_clausedef_t * const *clauseset; 1295 const cfg_map_t *mapsrc, *mapdst; 1296 isc_symtabaction_t saved_undef_action = NULL; 1297 void* saved_undef_arg = NULL; 1298 1299 REQUIRE(mapobjsrc != NULL && mapobjsrc->type->rep == &cfg_rep_map); 1300 REQUIRE(mapobjdst != NULL && mapobjdst->type->rep == &cfg_rep_map); 1301 REQUIRE(mapobjdst->type == mapobjsrc->type); 1302 1303 mapsrc = &mapobjsrc->value.map; 1304 mapdst = &mapobjdst->value.map; 1305 1306 /* Don't want the object to get destroyed when it is removed from 1307 * the old map. So temporarily set the undefine_action to NULL which 1308 * will cause the map to forget about the object without actually 1309 * destroying the object. 1310 */ 1311 isc_symtab_getundefineaction(mapsrc->symtab, &saved_undef_action, &saved_undef_arg); 1312 isc_symtab_setundefineaction(mapsrc->symtab, NULL, NULL); 1313 1314 for (clauseset = mapsrc->clausesets; *clauseset != NULL; clauseset++) { 1315 isc_symvalue_t symval; 1316 const cfg_clausedef_t *clause; 1317 1318 for (clause = *clauseset; clause->name != NULL; clause++) { 1319 result = isc_symtab_lookup(mapsrc->symtab, 1320 clause->name, 0, &symval); 1321 if (result == ISC_R_SUCCESS) { 1322 isc_symtab_undefine(mapsrc->symtab, clause->name, 1); 1323 CHECK(isc_symtab_define(mapdst->symtab, clause->name, 1324 1, symval, isc_symexists_replace)); 1325 } 1326 } 1327 } 1328 1329 result = ISC_R_SUCCESS; 1330 1331cleanup: 1332 isc_symtab_setundefineaction(mapsrc->symtab, saved_undef_action, saved_undef_arg); 1333 return result; 1334} 1335 1336/* 1337 * Parse a map body. That's something like 1338 * 1339 * "foo 1; bar { glub; }; zap true; zap false;" 1340 * 1341 * i.e., a sequence of option names followed by values and 1342 * terminated by semicolons. Used for the top level of 1343 * the named.conf syntax, as well as for the body of the 1344 * options, view, zone, and other statements. 1345 */ 1346isc_result_t 1347cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) 1348{ 1349 const cfg_clausedef_t * const *clausesets = type->of; 1350 isc_result_t result; 1351 const cfg_clausedef_t * const *clauseset; 1352 const cfg_clausedef_t *clause; 1353 cfg_obj_t *value = NULL; 1354 cfg_obj_t *obj = NULL; 1355 cfg_obj_t *eltobj = NULL; 1356 cfg_obj_t *includename = NULL; 1357 isc_symvalue_t symval; 1358 cfg_list_t *list = NULL; 1359 1360 CHECK(create_map(pctx, type, &obj)); 1361 1362 obj->value.map.clausesets = clausesets; 1363 1364 for (;;) { 1365 cfg_listelt_t *elt; 1366 1367 redo: 1368 /* 1369 * Parse the option name and see if it is known. 1370 */ 1371 CHECK(cfg_gettoken(pctx, 0)); 1372 1373 if (pctx->token.type != isc_tokentype_string) { 1374 cfg_ungettoken(pctx); 1375 break; 1376 } 1377 1378 /* 1379 * We accept "include" statements wherever a map body 1380 * clause can occur. 1381 */ 1382 if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) { 1383 /* 1384 * Turn the file name into a temporary configuration 1385 * object just so that it is not overwritten by the 1386 * semicolon token. 1387 */ 1388 CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename)); 1389 CHECK(parse_semicolon(pctx)); 1390 CHECK(parser_openfile(pctx, includename-> 1391 value.string.base)); 1392 cfg_obj_destroy(pctx, &includename); 1393 goto redo; 1394 } 1395 1396 clause = NULL; 1397 for (clauseset = clausesets; *clauseset != NULL; clauseset++) { 1398 for (clause = *clauseset; 1399 clause->name != NULL; 1400 clause++) { 1401 if (strcasecmp(TOKEN_STRING(pctx), 1402 clause->name) == 0) 1403 goto done; 1404 } 1405 } 1406 done: 1407 if (clause == NULL || clause->name == NULL) { 1408 cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option"); 1409 /* 1410 * Try to recover by parsing this option as an unknown 1411 * option and discarding it. 1412 */ 1413 CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj)); 1414 cfg_obj_destroy(pctx, &eltobj); 1415 CHECK(parse_semicolon(pctx)); 1416 continue; 1417 } 1418 1419 /* Clause is known. */ 1420 1421 /* Issue warnings if appropriate */ 1422 if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) 1423 cfg_parser_warning(pctx, 0, "option '%s' is obsolete", 1424 clause->name); 1425 if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0) 1426 cfg_parser_warning(pctx, 0, "option '%s' is " 1427 "not implemented", clause->name); 1428 if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0) 1429 cfg_parser_warning(pctx, 0, "option '%s' is " 1430 "not implemented", clause->name); 1431 1432 if ((clause->flags & CFG_CLAUSEFLAG_NOTCONFIGURED) != 0) { 1433 cfg_parser_warning(pctx, 0, "option '%s' is not " 1434 "configured", clause->name); 1435 result = ISC_R_FAILURE; 1436 goto cleanup; 1437 } 1438 1439 /* 1440 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT 1441 * set here - we need to log the *lack* of such an option, 1442 * not its presence. 1443 */ 1444 1445 /* See if the clause already has a value; if not create one. */ 1446 result = isc_symtab_lookup(obj->value.map.symtab, 1447 clause->name, 0, &symval); 1448 1449 if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) { 1450 /* Multivalued clause */ 1451 cfg_obj_t *listobj = NULL; 1452 if (result == ISC_R_NOTFOUND) { 1453 CHECK(cfg_create_list(pctx, 1454 &cfg_type_implicitlist, 1455 &listobj)); 1456 symval.as_pointer = listobj; 1457 result = isc_symtab_define(obj->value. 1458 map.symtab, 1459 clause->name, 1460 1, symval, 1461 isc_symexists_reject); 1462 if (result != ISC_R_SUCCESS) { 1463 cfg_parser_error(pctx, CFG_LOG_NEAR, 1464 "isc_symtab_define(%s) " 1465 "failed", clause->name); 1466 isc_mem_put(pctx->mctx, list, 1467 sizeof(cfg_list_t)); 1468 goto cleanup; 1469 } 1470 } else { 1471 INSIST(result == ISC_R_SUCCESS); 1472 listobj = symval.as_pointer; 1473 } 1474 1475 elt = NULL; 1476 CHECK(cfg_parse_listelt(pctx, clause->type, &elt)); 1477 CHECK(parse_semicolon(pctx)); 1478 1479 ISC_LIST_APPEND(listobj->value.list, elt, link); 1480 } else { 1481 /* Single-valued clause */ 1482 if (result == ISC_R_NOTFOUND) { 1483 isc_boolean_t callback = 1484 ISC_TF((clause->flags & 1485 CFG_CLAUSEFLAG_CALLBACK) != 0); 1486 CHECK(parse_symtab_elt(pctx, clause->name, 1487 clause->type, 1488 obj->value.map.symtab, 1489 callback)); 1490 CHECK(parse_semicolon(pctx)); 1491 } else if (result == ISC_R_SUCCESS) { 1492 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined", 1493 clause->name); 1494 result = ISC_R_EXISTS; 1495 goto cleanup; 1496 } else { 1497 cfg_parser_error(pctx, CFG_LOG_NEAR, 1498 "isc_symtab_define() failed"); 1499 goto cleanup; 1500 } 1501 } 1502 } 1503 1504 1505 *ret = obj; 1506 return (ISC_R_SUCCESS); 1507 1508 cleanup: 1509 CLEANUP_OBJ(value); 1510 CLEANUP_OBJ(obj); 1511 CLEANUP_OBJ(eltobj); 1512 CLEANUP_OBJ(includename); 1513 return (result); 1514} 1515 1516static isc_result_t 1517parse_symtab_elt(cfg_parser_t *pctx, const char *name, 1518 cfg_type_t *elttype, isc_symtab_t *symtab, 1519 isc_boolean_t callback) 1520{ 1521 isc_result_t result; 1522 cfg_obj_t *obj = NULL; 1523 isc_symvalue_t symval; 1524 1525 CHECK(cfg_parse_obj(pctx, elttype, &obj)); 1526 1527 if (callback && pctx->callback != NULL) 1528 CHECK(pctx->callback(name, obj, pctx->callbackarg)); 1529 1530 symval.as_pointer = obj; 1531 CHECK(isc_symtab_define(symtab, name, 1532 1, symval, 1533 isc_symexists_reject)); 1534 return (ISC_R_SUCCESS); 1535 1536 cleanup: 1537 CLEANUP_OBJ(obj); 1538 return (result); 1539} 1540 1541/* 1542 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }" 1543 */ 1544isc_result_t 1545cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1546 isc_result_t result; 1547 CHECK(cfg_parse_special(pctx, '{')); 1548 CHECK(cfg_parse_mapbody(pctx, type, ret)); 1549 CHECK(cfg_parse_special(pctx, '}')); 1550 cleanup: 1551 return (result); 1552} 1553 1554/* 1555 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map(). 1556 */ 1557static isc_result_t 1558parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type, 1559 cfg_obj_t **ret) 1560{ 1561 isc_result_t result; 1562 cfg_obj_t *idobj = NULL; 1563 cfg_obj_t *mapobj = NULL; 1564 1565 CHECK(cfg_parse_obj(pctx, nametype, &idobj)); 1566 CHECK(cfg_parse_map(pctx, type, &mapobj)); 1567 mapobj->value.map.id = idobj; 1568 idobj = NULL; 1569 *ret = mapobj; 1570 cleanup: 1571 CLEANUP_OBJ(idobj); 1572 return (result); 1573} 1574 1575/* 1576 * Parse a map identified by a string name. E.g., "name { foo 1; }". 1577 * Used for the "key" and "channel" statements. 1578 */ 1579isc_result_t 1580cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1581 return (parse_any_named_map(pctx, &cfg_type_astring, type, ret)); 1582} 1583 1584/* 1585 * Parse a map identified by a network address. 1586 * Used to be used for the "server" statement. 1587 */ 1588isc_result_t 1589cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1590 return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret)); 1591} 1592 1593/* 1594 * Parse a map identified by a network prefix. 1595 * Used for the "server" statement. 1596 */ 1597isc_result_t 1598cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1599 return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret)); 1600} 1601 1602void 1603cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1604 isc_result_t result = ISC_R_SUCCESS; 1605 1606 const cfg_clausedef_t * const *clauseset; 1607 1608 for (clauseset = obj->value.map.clausesets; 1609 *clauseset != NULL; 1610 clauseset++) 1611 { 1612 isc_symvalue_t symval; 1613 const cfg_clausedef_t *clause; 1614 1615 for (clause = *clauseset; 1616 clause->name != NULL; 1617 clause++) { 1618 result = isc_symtab_lookup(obj->value.map.symtab, 1619 clause->name, 0, &symval); 1620 if (result == ISC_R_SUCCESS) { 1621 cfg_obj_t *obj = symval.as_pointer; 1622 if (obj->type == &cfg_type_implicitlist) { 1623 /* Multivalued. */ 1624 cfg_list_t *list = &obj->value.list; 1625 cfg_listelt_t *elt; 1626 for (elt = ISC_LIST_HEAD(*list); 1627 elt != NULL; 1628 elt = ISC_LIST_NEXT(elt, link)) { 1629 print_indent(pctx); 1630 cfg_print_cstr(pctx, clause->name); 1631 cfg_print_chars(pctx, " ", 1); 1632 cfg_print_obj(pctx, elt->obj); 1633 cfg_print_chars(pctx, ";\n", 2); 1634 } 1635 } else { 1636 /* Single-valued. */ 1637 print_indent(pctx); 1638 cfg_print_cstr(pctx, clause->name); 1639 cfg_print_chars(pctx, " ", 1); 1640 cfg_print_obj(pctx, obj); 1641 cfg_print_chars(pctx, ";\n", 2); 1642 } 1643 } else if (result == ISC_R_NOTFOUND) { 1644 ; /* do nothing */ 1645 } else { 1646 INSIST(0); 1647 } 1648 } 1649 } 1650} 1651 1652void 1653cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) { 1654 const cfg_clausedef_t * const *clauseset; 1655 const cfg_clausedef_t *clause; 1656 1657 for (clauseset = type->of; *clauseset != NULL; clauseset++) { 1658 for (clause = *clauseset; 1659 clause->name != NULL; 1660 clause++) { 1661 cfg_print_cstr(pctx, clause->name); 1662 cfg_print_chars(pctx, " ", 1); 1663 cfg_doc_obj(pctx, clause->type); 1664 cfg_print_chars(pctx, ";", 1); 1665 /* XXX print flags here? */ 1666 cfg_print_chars(pctx, "\n\n", 2); 1667 } 1668 } 1669} 1670 1671static struct flagtext { 1672 unsigned int flag; 1673 const char *text; 1674} flagtexts[] = { 1675 { CFG_CLAUSEFLAG_NOTIMP, "not implemented" }, 1676 { CFG_CLAUSEFLAG_NYI, "not yet implemented" }, 1677 { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" }, 1678 { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" }, 1679 { CFG_CLAUSEFLAG_TESTONLY, "test only" }, 1680 { CFG_CLAUSEFLAG_NOTCONFIGURED, "not configured" }, 1681 { 0, NULL } 1682}; 1683 1684void 1685cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1686 if (obj->value.map.id != NULL) { 1687 cfg_print_obj(pctx, obj->value.map.id); 1688 cfg_print_chars(pctx, " ", 1); 1689 } 1690 print_open(pctx); 1691 cfg_print_mapbody(pctx, obj); 1692 print_close(pctx); 1693} 1694 1695static void 1696print_clause_flags(cfg_printer_t *pctx, unsigned int flags) { 1697 struct flagtext *p; 1698 isc_boolean_t first = ISC_TRUE; 1699 for (p = flagtexts; p->flag != 0; p++) { 1700 if ((flags & p->flag) != 0) { 1701 if (first) 1702 cfg_print_chars(pctx, " // ", 4); 1703 else 1704 cfg_print_chars(pctx, ", ", 2); 1705 cfg_print_cstr(pctx, p->text); 1706 first = ISC_FALSE; 1707 } 1708 } 1709} 1710 1711void 1712cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) { 1713 const cfg_clausedef_t * const *clauseset; 1714 const cfg_clausedef_t *clause; 1715 1716 if (type->parse == cfg_parse_named_map) { 1717 cfg_doc_obj(pctx, &cfg_type_astring); 1718 cfg_print_chars(pctx, " ", 1); 1719 } else if (type->parse == cfg_parse_addressed_map) { 1720 cfg_doc_obj(pctx, &cfg_type_netaddr); 1721 cfg_print_chars(pctx, " ", 1); 1722 } else if (type->parse == cfg_parse_netprefix_map) { 1723 cfg_doc_obj(pctx, &cfg_type_netprefix); 1724 cfg_print_chars(pctx, " ", 1); 1725 } 1726 1727 print_open(pctx); 1728 1729 for (clauseset = type->of; *clauseset != NULL; clauseset++) { 1730 for (clause = *clauseset; 1731 clause->name != NULL; 1732 clause++) { 1733 print_indent(pctx); 1734 cfg_print_cstr(pctx, clause->name); 1735 if (clause->type->print != cfg_print_void) 1736 cfg_print_chars(pctx, " ", 1); 1737 cfg_doc_obj(pctx, clause->type); 1738 cfg_print_chars(pctx, ";", 1); 1739 print_clause_flags(pctx, clause->flags); 1740 cfg_print_chars(pctx, "\n", 1); 1741 } 1742 } 1743 print_close(pctx); 1744} 1745 1746isc_boolean_t 1747cfg_obj_ismap(const cfg_obj_t *obj) { 1748 REQUIRE(obj != NULL); 1749 return (ISC_TF(obj->type->rep == &cfg_rep_map)); 1750} 1751 1752isc_result_t 1753cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) { 1754 isc_result_t result; 1755 isc_symvalue_t val; 1756 const cfg_map_t *map; 1757 1758 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 1759 REQUIRE(name != NULL); 1760 REQUIRE(obj != NULL && *obj == NULL); 1761 1762 map = &mapobj->value.map; 1763 1764 result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val); 1765 if (result != ISC_R_SUCCESS) 1766 return (result); 1767 *obj = val.as_pointer; 1768 return (ISC_R_SUCCESS); 1769} 1770 1771const cfg_obj_t * 1772cfg_map_getname(const cfg_obj_t *mapobj) { 1773 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 1774 return (mapobj->value.map.id); 1775} 1776 1777 1778/* Parse an arbitrary token, storing its raw text representation. */ 1779static isc_result_t 1780parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1781 cfg_obj_t *obj = NULL; 1782 isc_result_t result; 1783 isc_region_t r; 1784 1785 UNUSED(type); 1786 1787 CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj)); 1788 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); 1789 if (pctx->token.type == isc_tokentype_eof) { 1790 cfg_ungettoken(pctx); 1791 result = ISC_R_EOF; 1792 goto cleanup; 1793 } 1794 1795 isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r); 1796 1797 obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1); 1798 if (obj->value.string.base == NULL) { 1799 result = ISC_R_NOMEMORY; 1800 goto cleanup; 1801 } 1802 obj->value.string.length = r.length; 1803 memcpy(obj->value.string.base, r.base, r.length); 1804 obj->value.string.base[r.length] = '\0'; 1805 *ret = obj; 1806 return (result); 1807 1808 cleanup: 1809 if (obj != NULL) 1810 isc_mem_put(pctx->mctx, obj, sizeof(*obj)); 1811 return (result); 1812} 1813 1814cfg_type_t cfg_type_token = { 1815 "token", parse_token, cfg_print_ustring, cfg_doc_terminal, 1816 &cfg_rep_string, NULL 1817}; 1818 1819/* 1820 * An unsupported option. This is just a list of tokens with balanced braces 1821 * ending in a semicolon. 1822 */ 1823 1824static isc_result_t 1825parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1826 cfg_obj_t *listobj = NULL; 1827 isc_result_t result; 1828 int braces = 0; 1829 1830 CHECK(cfg_create_list(pctx, type, &listobj)); 1831 1832 for (;;) { 1833 cfg_listelt_t *elt = NULL; 1834 1835 CHECK(cfg_peektoken(pctx, 0)); 1836 if (pctx->token.type == isc_tokentype_special) { 1837 if (pctx->token.value.as_char == '{') 1838 braces++; 1839 else if (pctx->token.value.as_char == '}') 1840 braces--; 1841 else if (pctx->token.value.as_char == ';') 1842 if (braces == 0) 1843 break; 1844 } 1845 if (pctx->token.type == isc_tokentype_eof || braces < 0) { 1846 cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token"); 1847 result = ISC_R_UNEXPECTEDTOKEN; 1848 goto cleanup; 1849 } 1850 1851 CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt)); 1852 ISC_LIST_APPEND(listobj->value.list, elt, link); 1853 } 1854 INSIST(braces == 0); 1855 *ret = listobj; 1856 return (ISC_R_SUCCESS); 1857 1858 cleanup: 1859 CLEANUP_OBJ(listobj); 1860 return (result); 1861} 1862 1863cfg_type_t cfg_type_unsupported = { 1864 "unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal, 1865 &cfg_rep_list, NULL 1866}; 1867 1868/* 1869 * Try interpreting the current token as a network address. 1870 * 1871 * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard 1872 * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set. The 1873 * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is 1874 * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set), 1875 * and the IPv6 wildcard address otherwise. 1876 */ 1877static isc_result_t 1878token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) { 1879 char *s; 1880 struct in_addr in4a; 1881 struct in6_addr in6a; 1882 1883 if (pctx->token.type != isc_tokentype_string) 1884 return (ISC_R_UNEXPECTEDTOKEN); 1885 1886 s = TOKEN_STRING(pctx); 1887 if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) { 1888 if ((flags & CFG_ADDR_V4OK) != 0) { 1889 isc_netaddr_any(na); 1890 return (ISC_R_SUCCESS); 1891 } else if ((flags & CFG_ADDR_V6OK) != 0) { 1892 isc_netaddr_any6(na); 1893 return (ISC_R_SUCCESS); 1894 } else { 1895 INSIST(0); 1896 } 1897 } else { 1898 if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) { 1899 if (inet_pton(AF_INET, s, &in4a) == 1) { 1900 isc_netaddr_fromin(na, &in4a); 1901 return (ISC_R_SUCCESS); 1902 } 1903 } 1904 if ((flags & CFG_ADDR_V4PREFIXOK) != 0 && 1905 strlen(s) <= 15U) { 1906 char buf[64]; 1907 int i; 1908 1909 strcpy(buf, s); 1910 for (i = 0; i < 3; i++) { 1911 strcat(buf, ".0"); 1912 if (inet_pton(AF_INET, buf, &in4a) == 1) { 1913 isc_netaddr_fromin(na, &in4a); 1914 return (ISC_R_SUCCESS); 1915 } 1916 } 1917 } 1918 if ((flags & CFG_ADDR_V6OK) != 0 && 1919 strlen(s) <= 127U) { 1920 char buf[128]; /* see lib/bind9/getaddresses.c */ 1921 char *d; /* zone delimiter */ 1922 isc_uint32_t zone = 0; /* scope zone ID */ 1923 1924 strcpy(buf, s); 1925 d = strchr(buf, '%'); 1926 if (d != NULL) 1927 *d = '\0'; 1928 1929 if (inet_pton(AF_INET6, buf, &in6a) == 1) { 1930 if (d != NULL) { 1931#ifdef ISC_PLATFORM_HAVESCOPEID 1932 isc_result_t result; 1933 1934 result = isc_netscope_pton(AF_INET6, 1935 d + 1, 1936 &in6a, 1937 &zone); 1938 if (result != ISC_R_SUCCESS) 1939 return (result); 1940#else 1941 return (ISC_R_BADADDRESSFORM); 1942#endif 1943 } 1944 1945 isc_netaddr_fromin6(na, &in6a); 1946 isc_netaddr_setzone(na, zone); 1947 return (ISC_R_SUCCESS); 1948 } 1949 } 1950 } 1951 return (ISC_R_UNEXPECTEDTOKEN); 1952} 1953 1954isc_result_t 1955cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) { 1956 isc_result_t result; 1957 const char *wild = ""; 1958 const char *prefix = ""; 1959 1960 CHECK(cfg_gettoken(pctx, 0)); 1961 result = token_addr(pctx, flags, na); 1962 if (result == ISC_R_UNEXPECTEDTOKEN) { 1963 if ((flags & CFG_ADDR_WILDOK) != 0) 1964 wild = " or '*'"; 1965 if ((flags & CFG_ADDR_V4PREFIXOK) != 0) 1966 wild = " or IPv4 prefix"; 1967 if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK) 1968 cfg_parser_error(pctx, CFG_LOG_NEAR, 1969 "expected IPv4 address%s%s", 1970 prefix, wild); 1971 else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK) 1972 cfg_parser_error(pctx, CFG_LOG_NEAR, 1973 "expected IPv6 address%s%s", 1974 prefix, wild); 1975 else 1976 cfg_parser_error(pctx, CFG_LOG_NEAR, 1977 "expected IP address%s%s", 1978 prefix, wild); 1979 } 1980 cleanup: 1981 return (result); 1982} 1983 1984isc_boolean_t 1985cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) { 1986 isc_result_t result; 1987 isc_netaddr_t na_dummy; 1988 result = token_addr(pctx, flags, &na_dummy); 1989 return (ISC_TF(result == ISC_R_SUCCESS)); 1990} 1991 1992isc_result_t 1993cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) { 1994 isc_result_t result; 1995 1996 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER)); 1997 1998 if ((flags & CFG_ADDR_WILDOK) != 0 && 1999 pctx->token.type == isc_tokentype_string && 2000 strcmp(TOKEN_STRING(pctx), "*") == 0) { 2001 *port = 0; 2002 return (ISC_R_SUCCESS); 2003 } 2004 if (pctx->token.type != isc_tokentype_number) { 2005 cfg_parser_error(pctx, CFG_LOG_NEAR, 2006 "expected port number or '*'"); 2007 return (ISC_R_UNEXPECTEDTOKEN); 2008 } 2009 if (pctx->token.value.as_ulong >= 65536U) { 2010 cfg_parser_error(pctx, CFG_LOG_NEAR, 2011 "port number out of range"); 2012 return (ISC_R_UNEXPECTEDTOKEN); 2013 } 2014 *port = (in_port_t)(pctx->token.value.as_ulong); 2015 return (ISC_R_SUCCESS); 2016 cleanup: 2017 return (result); 2018} 2019 2020void 2021cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) { 2022 isc_result_t result; 2023 char text[128]; 2024 isc_buffer_t buf; 2025 2026 isc_buffer_init(&buf, text, sizeof(text)); 2027 result = isc_netaddr_totext(na, &buf); 2028 RUNTIME_CHECK(result == ISC_R_SUCCESS); 2029 cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf)); 2030} 2031 2032/* netaddr */ 2033 2034static unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK; 2035static unsigned int netaddr4_flags = CFG_ADDR_V4OK; 2036static unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK; 2037static unsigned int netaddr6_flags = CFG_ADDR_V6OK; 2038static unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK; 2039 2040static isc_result_t 2041parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 2042 isc_result_t result; 2043 cfg_obj_t *obj = NULL; 2044 isc_netaddr_t netaddr; 2045 unsigned int flags = *(const unsigned int *)type->of; 2046 2047 CHECK(cfg_create_obj(pctx, type, &obj)); 2048 CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr)); 2049 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0); 2050 *ret = obj; 2051 return (ISC_R_SUCCESS); 2052 cleanup: 2053 CLEANUP_OBJ(obj); 2054 return (result); 2055} 2056 2057static void 2058cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) { 2059 const unsigned int *flagp = type->of; 2060 int n = 0; 2061 if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK) 2062 cfg_print_chars(pctx, "( ", 2); 2063 if (*flagp & CFG_ADDR_V4OK) { 2064 cfg_print_cstr(pctx, "<ipv4_address>"); 2065 n++; 2066 } 2067 if (*flagp & CFG_ADDR_V6OK) { 2068 if (n != 0) 2069 cfg_print_chars(pctx, " | ", 3); 2070 cfg_print_cstr(pctx, "<ipv6_address>"); 2071 n++; 2072 } 2073 if (*flagp & CFG_ADDR_WILDOK) { 2074 if (n != 0) 2075 cfg_print_chars(pctx, " | ", 3); 2076 cfg_print_chars(pctx, "*", 1); 2077 n++; 2078 POST(n); 2079 } 2080 if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK) 2081 cfg_print_chars(pctx, " )", 2); 2082} 2083 2084cfg_type_t cfg_type_netaddr = { 2085 "netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr, 2086 &cfg_rep_sockaddr, &netaddr_flags 2087}; 2088 2089cfg_type_t cfg_type_netaddr4 = { 2090 "netaddr4", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr, 2091 &cfg_rep_sockaddr, &netaddr4_flags 2092}; 2093 2094cfg_type_t cfg_type_netaddr4wild = { 2095 "netaddr4wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr, 2096 &cfg_rep_sockaddr, &netaddr4wild_flags 2097}; 2098 2099cfg_type_t cfg_type_netaddr6 = { 2100 "netaddr6", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr, 2101 &cfg_rep_sockaddr, &netaddr6_flags 2102}; 2103 2104cfg_type_t cfg_type_netaddr6wild = { 2105 "netaddr6wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr, 2106 &cfg_rep_sockaddr, &netaddr6wild_flags 2107}; 2108 2109/* netprefix */ 2110 2111isc_result_t 2112cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type, 2113 cfg_obj_t **ret) 2114{ 2115 cfg_obj_t *obj = NULL; 2116 isc_result_t result; 2117 isc_netaddr_t netaddr; 2118 unsigned int addrlen = 0, prefixlen; 2119 UNUSED(type); 2120 2121 CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK | 2122 CFG_ADDR_V6OK, &netaddr)); 2123 switch (netaddr.family) { 2124 case AF_INET: 2125 addrlen = 32; 2126 break; 2127 case AF_INET6: 2128 addrlen = 128; 2129 break; 2130 default: 2131 INSIST(0); 2132 break; 2133 } 2134 CHECK(cfg_peektoken(pctx, 0)); 2135 if (pctx->token.type == isc_tokentype_special && 2136 pctx->token.value.as_char == '/') { 2137 CHECK(cfg_gettoken(pctx, 0)); /* read "/" */ 2138 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER)); 2139 if (pctx->token.type != isc_tokentype_number) { 2140 cfg_parser_error(pctx, CFG_LOG_NEAR, 2141 "expected prefix length"); 2142 return (ISC_R_UNEXPECTEDTOKEN); 2143 } 2144 prefixlen = pctx->token.value.as_ulong; 2145 if (prefixlen > addrlen) { 2146 cfg_parser_error(pctx, CFG_LOG_NOPREP, 2147 "invalid prefix length"); 2148 return (ISC_R_RANGE); 2149 } 2150 } else { 2151 prefixlen = addrlen; 2152 } 2153 CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj)); 2154 obj->value.netprefix.address = netaddr; 2155 obj->value.netprefix.prefixlen = prefixlen; 2156 *ret = obj; 2157 return (ISC_R_SUCCESS); 2158 cleanup: 2159 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix"); 2160 return (result); 2161} 2162 2163static void 2164print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) { 2165 const cfg_netprefix_t *p = &obj->value.netprefix; 2166 2167 cfg_print_rawaddr(pctx, &p->address); 2168 cfg_print_chars(pctx, "/", 1); 2169 cfg_print_rawuint(pctx, p->prefixlen); 2170} 2171 2172isc_boolean_t 2173cfg_obj_isnetprefix(const cfg_obj_t *obj) { 2174 REQUIRE(obj != NULL); 2175 return (ISC_TF(obj->type->rep == &cfg_rep_netprefix)); 2176} 2177 2178void 2179cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr, 2180 unsigned int *prefixlen) 2181{ 2182 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix); 2183 REQUIRE(netaddr != NULL); 2184 REQUIRE(prefixlen != NULL); 2185 2186 *netaddr = obj->value.netprefix.address; 2187 *prefixlen = obj->value.netprefix.prefixlen; 2188} 2189 2190cfg_type_t cfg_type_netprefix = { 2191 "netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal, 2192 &cfg_rep_netprefix, NULL 2193}; 2194 2195static isc_result_t 2196parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type, 2197 int flags, cfg_obj_t **ret) 2198{ 2199 isc_result_t result; 2200 isc_netaddr_t netaddr; 2201 in_port_t port = 0; 2202 cfg_obj_t *obj = NULL; 2203 2204 CHECK(cfg_create_obj(pctx, type, &obj)); 2205 CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr)); 2206 CHECK(cfg_peektoken(pctx, 0)); 2207 if (pctx->token.type == isc_tokentype_string && 2208 strcasecmp(TOKEN_STRING(pctx), "port") == 0) { 2209 CHECK(cfg_gettoken(pctx, 0)); /* read "port" */ 2210 CHECK(cfg_parse_rawport(pctx, flags, &port)); 2211 } 2212 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port); 2213 *ret = obj; 2214 return (ISC_R_SUCCESS); 2215 2216 cleanup: 2217 CLEANUP_OBJ(obj); 2218 return (result); 2219} 2220 2221static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK; 2222cfg_type_t cfg_type_sockaddr = { 2223 "sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr, 2224 &cfg_rep_sockaddr, &sockaddr_flags 2225}; 2226 2227isc_result_t 2228cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 2229 const unsigned int *flagp = type->of; 2230 return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret)); 2231} 2232 2233void 2234cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) { 2235 isc_netaddr_t netaddr; 2236 in_port_t port; 2237 char buf[ISC_NETADDR_FORMATSIZE]; 2238 2239 isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr); 2240 isc_netaddr_format(&netaddr, buf, sizeof(buf)); 2241 cfg_print_cstr(pctx, buf); 2242 port = isc_sockaddr_getport(&obj->value.sockaddr); 2243 if (port != 0) { 2244 cfg_print_chars(pctx, " port ", 6); 2245 cfg_print_rawuint(pctx, port); 2246 } 2247} 2248 2249void 2250cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) { 2251 const unsigned int *flagp = type->of; 2252 int n = 0; 2253 cfg_print_chars(pctx, "( ", 2); 2254 if (*flagp & CFG_ADDR_V4OK) { 2255 cfg_print_cstr(pctx, "<ipv4_address>"); 2256 n++; 2257 } 2258 if (*flagp & CFG_ADDR_V6OK) { 2259 if (n != 0) 2260 cfg_print_chars(pctx, " | ", 3); 2261 cfg_print_cstr(pctx, "<ipv6_address>"); 2262 n++; 2263 } 2264 if (*flagp & CFG_ADDR_WILDOK) { 2265 if (n != 0) 2266 cfg_print_chars(pctx, " | ", 3); 2267 cfg_print_chars(pctx, "*", 1); 2268 n++; 2269 POST(n); 2270 } 2271 cfg_print_chars(pctx, " ) ", 3); 2272 if (*flagp & CFG_ADDR_WILDOK) { 2273 cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]"); 2274 } else { 2275 cfg_print_cstr(pctx, "[ port <integer> ]"); 2276 } 2277} 2278 2279isc_boolean_t 2280cfg_obj_issockaddr(const cfg_obj_t *obj) { 2281 REQUIRE(obj != NULL); 2282 return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr)); 2283} 2284 2285const isc_sockaddr_t * 2286cfg_obj_assockaddr(const cfg_obj_t *obj) { 2287 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr); 2288 return (&obj->value.sockaddr); 2289} 2290 2291isc_result_t 2292cfg_gettoken(cfg_parser_t *pctx, int options) { 2293 isc_result_t result; 2294 2295 if (pctx->seen_eof) 2296 return (ISC_R_SUCCESS); 2297 2298 options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE); 2299 2300 redo: 2301 pctx->token.type = isc_tokentype_unknown; 2302 result = isc_lex_gettoken(pctx->lexer, options, &pctx->token); 2303 pctx->ungotten = ISC_FALSE; 2304 pctx->line = isc_lex_getsourceline(pctx->lexer); 2305 2306 switch (result) { 2307 case ISC_R_SUCCESS: 2308 if (pctx->token.type == isc_tokentype_eof) { 2309 result = isc_lex_close(pctx->lexer); 2310 INSIST(result == ISC_R_NOMORE || 2311 result == ISC_R_SUCCESS); 2312 2313 if (isc_lex_getsourcename(pctx->lexer) != NULL) { 2314 /* 2315 * Closed an included file, not the main file. 2316 */ 2317 cfg_listelt_t *elt; 2318 elt = ISC_LIST_TAIL(pctx->open_files-> 2319 value.list); 2320 INSIST(elt != NULL); 2321 ISC_LIST_UNLINK(pctx->open_files-> 2322 value.list, elt, link); 2323 ISC_LIST_APPEND(pctx->closed_files-> 2324 value.list, elt, link); 2325 goto redo; 2326 } 2327 pctx->seen_eof = ISC_TRUE; 2328 } 2329 break; 2330 2331 case ISC_R_NOSPACE: 2332 /* More understandable than "ran out of space". */ 2333 cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big"); 2334 break; 2335 2336 case ISC_R_IOERROR: 2337 cfg_parser_error(pctx, 0, "%s", 2338 isc_result_totext(result)); 2339 break; 2340 2341 default: 2342 cfg_parser_error(pctx, CFG_LOG_NEAR, "%s", 2343 isc_result_totext(result)); 2344 break; 2345 } 2346 return (result); 2347} 2348 2349void 2350cfg_ungettoken(cfg_parser_t *pctx) { 2351 if (pctx->seen_eof) 2352 return; 2353 isc_lex_ungettoken(pctx->lexer, &pctx->token); 2354 pctx->ungotten = ISC_TRUE; 2355} 2356 2357isc_result_t 2358cfg_peektoken(cfg_parser_t *pctx, int options) { 2359 isc_result_t result; 2360 CHECK(cfg_gettoken(pctx, options)); 2361 cfg_ungettoken(pctx); 2362 cleanup: 2363 return (result); 2364} 2365 2366/* 2367 * Get a string token, accepting both the quoted and the unquoted form. 2368 * Log an error if the next token is not a string. 2369 */ 2370static isc_result_t 2371cfg_getstringtoken(cfg_parser_t *pctx) { 2372 isc_result_t result; 2373 2374 result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING); 2375 if (result != ISC_R_SUCCESS) 2376 return (result); 2377 2378 if (pctx->token.type != isc_tokentype_string && 2379 pctx->token.type != isc_tokentype_qstring) { 2380 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string"); 2381 return (ISC_R_UNEXPECTEDTOKEN); 2382 } 2383 return (ISC_R_SUCCESS); 2384} 2385 2386void 2387cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) { 2388 va_list args; 2389 va_start(args, fmt); 2390 parser_complain(pctx, ISC_FALSE, flags, fmt, args); 2391 va_end(args); 2392 pctx->errors++; 2393} 2394 2395void 2396cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) { 2397 va_list args; 2398 va_start(args, fmt); 2399 parser_complain(pctx, ISC_TRUE, flags, fmt, args); 2400 va_end(args); 2401 pctx->warnings++; 2402} 2403 2404#define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */ 2405 2406static isc_boolean_t 2407have_current_file(cfg_parser_t *pctx) { 2408 cfg_listelt_t *elt; 2409 if (pctx->open_files == NULL) 2410 return (ISC_FALSE); 2411 2412 elt = ISC_LIST_TAIL(pctx->open_files->value.list); 2413 if (elt == NULL) 2414 return (ISC_FALSE); 2415 2416 return (ISC_TRUE); 2417} 2418 2419static char * 2420current_file(cfg_parser_t *pctx) { 2421 static char none[] = "none"; 2422 cfg_listelt_t *elt; 2423 cfg_obj_t *fileobj; 2424 2425 if (!have_current_file(pctx)) 2426 return (none); 2427 2428 elt = ISC_LIST_TAIL(pctx->open_files->value.list); 2429 if (elt == NULL) /* shouldn't be possible, but... */ 2430 return (none); 2431 2432 fileobj = elt->obj; 2433 INSIST(fileobj->type == &cfg_type_qstring); 2434 return (fileobj->value.string.base); 2435} 2436 2437static void 2438parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning, 2439 unsigned int flags, const char *format, 2440 va_list args) 2441{ 2442 char tokenbuf[MAX_LOG_TOKEN + 10]; 2443 static char where[ISC_DIR_PATHMAX + 100]; 2444 static char message[2048]; 2445 int level = ISC_LOG_ERROR; 2446 const char *prep = ""; 2447 size_t len; 2448 2449 if (is_warning) 2450 level = ISC_LOG_WARNING; 2451 2452 where[0] = '\0'; 2453 if (have_current_file(pctx)) 2454 snprintf(where, sizeof(where), "%s:%u: ", 2455 current_file(pctx), pctx->line); 2456 2457 len = vsnprintf(message, sizeof(message), format, args); 2458 if (len >= sizeof(message)) 2459 FATAL_ERROR(__FILE__, __LINE__, 2460 "error message would overflow"); 2461 2462 if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) { 2463 isc_region_t r; 2464 2465 if (pctx->ungotten) 2466 (void)cfg_gettoken(pctx, 0); 2467 2468 if (pctx->token.type == isc_tokentype_eof) { 2469 snprintf(tokenbuf, sizeof(tokenbuf), "end of file"); 2470 } else if (pctx->token.type == isc_tokentype_unknown) { 2471 flags = 0; 2472 tokenbuf[0] = '\0'; 2473 } else { 2474 isc_lex_getlasttokentext(pctx->lexer, 2475 &pctx->token, &r); 2476 if (r.length > MAX_LOG_TOKEN) 2477 snprintf(tokenbuf, sizeof(tokenbuf), 2478 "'%.*s...'", MAX_LOG_TOKEN, r.base); 2479 else 2480 snprintf(tokenbuf, sizeof(tokenbuf), 2481 "'%.*s'", (int)r.length, r.base); 2482 } 2483 2484 /* Choose a preposition. */ 2485 if (flags & CFG_LOG_NEAR) 2486 prep = " near "; 2487 else if (flags & CFG_LOG_BEFORE) 2488 prep = " before "; 2489 else 2490 prep = " "; 2491 } else { 2492 tokenbuf[0] = '\0'; 2493 } 2494 isc_log_write(pctx->lctx, CAT, MOD, level, 2495 "%s%s%s%s", where, message, prep, tokenbuf); 2496} 2497 2498void 2499cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level, 2500 const char *fmt, ...) { 2501 va_list ap; 2502 char msgbuf[2048]; 2503 2504 if (! isc_log_wouldlog(lctx, level)) 2505 return; 2506 2507 va_start(ap, fmt); 2508 2509 vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); 2510 isc_log_write(lctx, CAT, MOD, level, 2511 "%s:%u: %s", 2512 obj->file == NULL ? "<unknown file>" : obj->file, 2513 obj->line, msgbuf); 2514 va_end(ap); 2515} 2516 2517const char * 2518cfg_obj_file(const cfg_obj_t *obj) { 2519 return (obj->file); 2520} 2521 2522unsigned int 2523cfg_obj_line(const cfg_obj_t *obj) { 2524 return (obj->line); 2525} 2526 2527isc_result_t 2528cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 2529 isc_result_t result; 2530 cfg_obj_t *obj; 2531 2532 obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t)); 2533 if (obj == NULL) 2534 return (ISC_R_NOMEMORY); 2535 obj->type = type; 2536 obj->file = current_file(pctx); 2537 obj->line = pctx->line; 2538 result = isc_refcount_init(&obj->references, 1); 2539 if (result != ISC_R_SUCCESS) { 2540 isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t)); 2541 return (result); 2542 } 2543 *ret = obj; 2544 return (ISC_R_SUCCESS); 2545} 2546 2547 2548static void 2549map_symtabitem_destroy(char *key, unsigned int type, 2550 isc_symvalue_t symval, void *userarg) 2551{ 2552 cfg_obj_t *obj = symval.as_pointer; 2553 cfg_parser_t *pctx = (cfg_parser_t *)userarg; 2554 2555 UNUSED(key); 2556 UNUSED(type); 2557 2558 cfg_obj_destroy(pctx, &obj); 2559} 2560 2561 2562static isc_result_t 2563create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 2564 isc_result_t result; 2565 isc_symtab_t *symtab = NULL; 2566 cfg_obj_t *obj = NULL; 2567 2568 CHECK(cfg_create_obj(pctx, type, &obj)); 2569 CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */ 2570 map_symtabitem_destroy, 2571 pctx, ISC_FALSE, &symtab)); 2572 obj->value.map.symtab = symtab; 2573 obj->value.map.id = NULL; 2574 2575 *ret = obj; 2576 return (ISC_R_SUCCESS); 2577 2578 cleanup: 2579 if (obj != NULL) 2580 isc_mem_put(pctx->mctx, obj, sizeof(*obj)); 2581 return (result); 2582} 2583 2584static void 2585free_map(cfg_parser_t *pctx, cfg_obj_t *obj) { 2586 CLEANUP_OBJ(obj->value.map.id); 2587 isc_symtab_destroy(&obj->value.map.symtab); 2588} 2589 2590isc_boolean_t 2591cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) { 2592 return (ISC_TF(obj->type == type)); 2593} 2594 2595/* 2596 * Destroy 'obj', a configuration object created in 'pctx'. 2597 */ 2598void 2599cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) { 2600 cfg_obj_t *obj = *objp; 2601 unsigned int refs; 2602 2603 isc_refcount_decrement(&obj->references, &refs); 2604 if (refs == 0) { 2605 obj->type->rep->free(pctx, obj); 2606 isc_refcount_destroy(&obj->references); 2607 isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t)); 2608 } 2609 *objp = NULL; 2610} 2611 2612void 2613cfg_obj_attach(cfg_obj_t *src, cfg_obj_t **dest) { 2614 REQUIRE(src != NULL); 2615 REQUIRE(dest != NULL && *dest == NULL); 2616 isc_refcount_increment(&src->references, NULL); 2617 *dest = src; 2618} 2619 2620static void 2621free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) { 2622 UNUSED(pctx); 2623 UNUSED(obj); 2624} 2625 2626void 2627cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) { 2628 type->doc(pctx, type); 2629} 2630 2631void 2632cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) { 2633 cfg_print_chars(pctx, "<", 1); 2634 cfg_print_cstr(pctx, type->name); 2635 cfg_print_chars(pctx, ">", 1); 2636} 2637 2638void 2639cfg_print_grammar(const cfg_type_t *type, 2640 void (*f)(void *closure, const char *text, int textlen), 2641 void *closure) 2642{ 2643 cfg_printer_t pctx; 2644 pctx.f = f; 2645 pctx.closure = closure; 2646 pctx.indent = 0; 2647 cfg_doc_obj(&pctx, type); 2648} 2649