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