1 /* 2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 * PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /*! \file */ 18 19 #include <limits.h> 20 #include <stdlib.h> 21 #include <string.h> 22 23 #include <isc/lex.h> 24 #include <isc/log.h> 25 #include <isc/symtab.h> 26 #include <isc/util.h> 27 28 #include <isccfg/cfg.h> 29 #include <isccfg/grammar.h> 30 31 /*! 32 * Pass one of these flags to cfg_parser_error() to include the 33 * token text in log message. 34 */ 35 #define CFG_LOG_NEAR 0x00000001 /*%< Say "near <token>" */ 36 #define CFG_LOG_BEFORE 0x00000002 /*%< Say "before <token>" */ 37 #define CFG_LOG_NOPREP 0x00000004 /*%< Say just "<token>" */ 38 39 isc_logcategory_t cfg_category = { "config", 0 }; 40 isc_logmodule_t cfg_module = { "isccfg/parser", 0 }; 41 42 /* Shorthand */ 43 #define CAT &cfg_category 44 #define MOD &cfg_module 45 46 #define MAP_SYM 1 /* Unique type for isc_symtab */ 47 48 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base) 49 50 #define CFG_LEXOPT_QSTRING (ISC_LEXOPT_QSTRING | ISC_LEXOPT_QSTRINGMULTILINE) 51 52 /* Check a return value. */ 53 #define CHECK(op) \ 54 do { result = (op); \ 55 if (result != ISC_R_SUCCESS) goto cleanup; \ 56 } while (0) 57 58 /* Clean up a configuration object if non-NULL. */ 59 #define CLEANUP_OBJ(obj) \ 60 do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0) 61 62 /* Forward declarations of variables */ 63 cfg_rep_t cfg_rep_string; 64 cfg_rep_t cfg_rep_list; 65 66 cfg_type_t cfg_type_qstring; 67 cfg_type_t cfg_type_sstring; 68 cfg_type_t cfg_type_token; 69 cfg_type_t cfg_type_unsupported; 70 71 /* 72 * Forward declarations of static functions. 73 */ 74 75 static isc_result_t 76 cfg_gettoken(cfg_parser_t *pctx, int options); 77 78 static isc_result_t 79 cfg_peektoken(cfg_parser_t *pctx, int options); 80 81 static void 82 cfg_ungettoken(cfg_parser_t *pctx); 83 84 static isc_result_t 85 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp); 86 87 static isc_result_t 88 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); 89 90 static isc_result_t 91 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); 92 93 static isc_result_t 94 cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); 95 96 static isc_result_t 97 cfg_parse_special(cfg_parser_t *pctx, int special); 98 /*%< Parse a required special character 'special'. */ 99 100 static isc_result_t 101 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp); 102 103 static isc_result_t 104 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype, 105 cfg_listelt_t **ret); 106 107 static isc_result_t 108 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); 109 110 static isc_result_t 111 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); 112 113 static void 114 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, 115 const char *fmt, ...) __attribute__((__format__(__printf__, 3, 4))); 116 117 static void 118 free_list(cfg_parser_t *pctx, cfg_obj_t *obj); 119 120 static isc_result_t 121 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp); 122 123 static isc_result_t 124 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type, 125 cfg_obj_t **ret); 126 127 static void 128 free_string(cfg_parser_t *pctx, cfg_obj_t *obj); 129 130 static isc_result_t 131 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp); 132 133 static void 134 free_map(cfg_parser_t *pctx, cfg_obj_t *obj); 135 136 static isc_result_t 137 parse_symtab_elt(cfg_parser_t *pctx, const char *name, 138 cfg_type_t *elttype, isc_symtab_t *symtab); 139 140 static isc_result_t 141 cfg_getstringtoken(cfg_parser_t *pctx); 142 143 static void 144 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning, 145 unsigned int flags, const char *format, va_list args); 146 147 /* 148 * Data representations. These correspond to members of the 149 * "value" union in struct cfg_obj (except "void", which does 150 * not need a union member). 151 */ 152 153 cfg_rep_t cfg_rep_string = { "string", free_string }; 154 cfg_rep_t cfg_rep_map = { "map", free_map }; 155 cfg_rep_t cfg_rep_list = { "list", free_list }; 156 157 /* 158 * Configuration type definitions. 159 */ 160 161 /* Functions. */ 162 163 static isc_result_t 164 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 165 isc_result_t result; 166 167 REQUIRE(pctx != NULL); 168 REQUIRE(type != NULL); 169 REQUIRE(ret != NULL && *ret == NULL); 170 171 result = type->parse(pctx, type, ret); 172 if (result != ISC_R_SUCCESS) 173 return (result); 174 ENSURE(*ret != NULL); 175 return (ISC_R_SUCCESS); 176 } 177 178 static isc_result_t 179 cfg_parse_special(cfg_parser_t *pctx, int special) { 180 isc_result_t result; 181 182 REQUIRE(pctx != NULL); 183 184 CHECK(cfg_gettoken(pctx, 0)); 185 if (pctx->token.type == isc_tokentype_special && 186 pctx->token.value.as_char == special) 187 return (ISC_R_SUCCESS); 188 189 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special); 190 return (ISC_R_UNEXPECTEDTOKEN); 191 cleanup: 192 return (result); 193 } 194 195 /* 196 * Parse a required semicolon. If it is not there, log 197 * an error and increment the error count but continue 198 * parsing. Since the next token is pushed back, 199 * care must be taken to make sure it is eventually 200 * consumed or an infinite loop may result. 201 */ 202 static isc_result_t 203 parse_semicolon(cfg_parser_t *pctx) { 204 isc_result_t result; 205 206 CHECK(cfg_gettoken(pctx, 0)); 207 if (pctx->token.type == isc_tokentype_special && 208 pctx->token.value.as_char == ';') 209 return (ISC_R_SUCCESS); 210 211 cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'"); 212 cfg_ungettoken(pctx); 213 cleanup: 214 return (result); 215 } 216 217 /* 218 * Parse EOF, logging and returning an error if not there. 219 */ 220 static isc_result_t 221 parse_eof(cfg_parser_t *pctx) { 222 isc_result_t result; 223 224 CHECK(cfg_gettoken(pctx, 0)); 225 226 if (pctx->token.type == isc_tokentype_eof) 227 return (ISC_R_SUCCESS); 228 229 cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error"); 230 return (ISC_R_UNEXPECTEDTOKEN); 231 cleanup: 232 return (result); 233 } 234 235 /* A list of files, used internally for pctx->files. */ 236 237 static cfg_type_t cfg_type_filelist = { 238 "filelist", NULL, &cfg_rep_list, 239 &cfg_type_qstring 240 }; 241 242 isc_result_t 243 cfg_parser_create(isc_log_t *lctx, cfg_parser_t **ret) { 244 isc_result_t result; 245 cfg_parser_t *pctx; 246 isc_lexspecials_t specials; 247 248 REQUIRE(ret != NULL && *ret == NULL); 249 250 pctx = malloc(sizeof(*pctx)); 251 if (pctx == NULL) 252 return (ISC_R_NOMEMORY); 253 254 result = isc_refcount_init(&pctx->references, 1); 255 if (result != ISC_R_SUCCESS) { 256 free(pctx); 257 return (result); 258 } 259 260 pctx->lctx = lctx; 261 pctx->lexer = NULL; 262 pctx->seen_eof = ISC_FALSE; 263 pctx->ungotten = ISC_FALSE; 264 pctx->errors = 0; 265 pctx->open_files = NULL; 266 pctx->closed_files = NULL; 267 pctx->line = 0; 268 pctx->token.type = isc_tokentype_unknown; 269 pctx->flags = 0; 270 271 memset(specials, 0, sizeof(specials)); 272 specials['{'] = 1; 273 specials['}'] = 1; 274 specials[';'] = 1; 275 specials['/'] = 1; 276 specials['"'] = 1; 277 specials['!'] = 1; 278 279 CHECK(isc_lex_create(1024, &pctx->lexer)); 280 281 isc_lex_setspecials(pctx->lexer, specials); 282 isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C | 283 ISC_LEXCOMMENT_CPLUSPLUS | 284 ISC_LEXCOMMENT_SHELL)); 285 286 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files)); 287 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files)); 288 289 *ret = pctx; 290 return (ISC_R_SUCCESS); 291 292 cleanup: 293 if (pctx->lexer != NULL) 294 isc_lex_destroy(&pctx->lexer); 295 CLEANUP_OBJ(pctx->open_files); 296 CLEANUP_OBJ(pctx->closed_files); 297 free(pctx); 298 return (result); 299 } 300 301 static isc_result_t 302 parser_openfile(cfg_parser_t *pctx, const char *filename) { 303 isc_result_t result; 304 cfg_listelt_t *elt = NULL; 305 cfg_obj_t *stringobj = NULL; 306 307 result = isc_lex_openfile(pctx->lexer, filename); 308 if (result != ISC_R_SUCCESS) { 309 cfg_parser_error(pctx, 0, "open: %s: %s", 310 filename, isc_result_totext(result)); 311 goto cleanup; 312 } 313 314 CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj)); 315 CHECK(create_listelt(pctx, &elt)); 316 elt->obj = stringobj; 317 ISC_LIST_APPEND(pctx->open_files->value.list, elt, link); 318 319 return (ISC_R_SUCCESS); 320 cleanup: 321 CLEANUP_OBJ(stringobj); 322 return (result); 323 } 324 325 /* 326 * Parse a configuration using a pctx where a lexer has already 327 * been set up with a source. 328 */ 329 static isc_result_t 330 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 331 isc_result_t result; 332 cfg_obj_t *obj = NULL; 333 334 result = cfg_parse_obj(pctx, type, &obj); 335 336 if (pctx->errors != 0) { 337 /* Errors have been logged. */ 338 if (result == ISC_R_SUCCESS) 339 result = ISC_R_FAILURE; 340 goto cleanup; 341 } 342 343 if (result != ISC_R_SUCCESS) { 344 /* Parsing failed but no errors have been logged. */ 345 cfg_parser_error(pctx, 0, "parsing failed: %s", 346 isc_result_totext(result)); 347 goto cleanup; 348 } 349 350 CHECK(parse_eof(pctx)); 351 352 *ret = obj; 353 return (ISC_R_SUCCESS); 354 355 cleanup: 356 CLEANUP_OBJ(obj); 357 return (result); 358 } 359 360 isc_result_t 361 cfg_parse_file(cfg_parser_t *pctx, const char *filename, 362 const cfg_type_t *type, cfg_obj_t **ret) 363 { 364 isc_result_t result; 365 366 REQUIRE(pctx != NULL); 367 REQUIRE(filename != NULL); 368 REQUIRE(type != NULL); 369 REQUIRE(ret != NULL && *ret == NULL); 370 371 CHECK(parser_openfile(pctx, filename)); 372 CHECK(parse2(pctx, type, ret)); 373 cleanup: 374 return (result); 375 } 376 377 void 378 cfg_parser_destroy(cfg_parser_t **pctxp) { 379 cfg_parser_t *pctx; 380 unsigned int refs; 381 382 REQUIRE(pctxp != NULL && *pctxp != NULL); 383 384 pctx = *pctxp; 385 *pctxp = NULL; 386 387 isc_refcount_decrement(&pctx->references, &refs); 388 if (refs == 0) { 389 isc_lex_destroy(&pctx->lexer); 390 /* 391 * Cleaning up open_files does not 392 * close the files; that was already done 393 * by closing the lexer. 394 */ 395 CLEANUP_OBJ(pctx->open_files); 396 CLEANUP_OBJ(pctx->closed_files); 397 free(pctx); 398 } 399 } 400 401 /* 402 * qstring (quoted string), ustring (unquoted string), astring 403 * (any string) 404 */ 405 406 /* Create a string object from a null-terminated C string. */ 407 static isc_result_t 408 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type, 409 cfg_obj_t **ret) 410 { 411 isc_result_t result; 412 cfg_obj_t *obj = NULL; 413 int len; 414 415 CHECK(cfg_create_obj(pctx, type, &obj)); 416 len = strlen(contents); 417 obj->value.string.length = len; 418 obj->value.string.base = malloc(len + 1); 419 if (obj->value.string.base == NULL) { 420 free(obj); 421 return (ISC_R_NOMEMORY); 422 } 423 memmove(obj->value.string.base, contents, len); 424 obj->value.string.base[len] = '\0'; 425 426 *ret = obj; 427 cleanup: 428 return (result); 429 } 430 431 static isc_result_t 432 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 433 isc_result_t result; 434 435 REQUIRE(pctx != NULL); 436 REQUIRE(ret != NULL && *ret == NULL); 437 438 UNUSED(type); 439 440 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); 441 if (pctx->token.type != isc_tokentype_qstring) { 442 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string"); 443 return (ISC_R_UNEXPECTEDTOKEN); 444 } 445 return (create_string(pctx, TOKEN_STRING(pctx), 446 &cfg_type_qstring, ret)); 447 cleanup: 448 return (result); 449 } 450 451 static isc_result_t 452 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, 453 cfg_obj_t **ret) 454 { 455 isc_result_t result; 456 457 REQUIRE(pctx != NULL); 458 REQUIRE(ret != NULL && *ret == NULL); 459 460 UNUSED(type); 461 462 CHECK(cfg_getstringtoken(pctx)); 463 return (create_string(pctx, 464 TOKEN_STRING(pctx), 465 &cfg_type_qstring, 466 ret)); 467 cleanup: 468 return (result); 469 } 470 471 static isc_result_t 472 cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type, 473 cfg_obj_t **ret) 474 { 475 isc_result_t result; 476 477 REQUIRE(pctx != NULL); 478 REQUIRE(ret != NULL && *ret == NULL); 479 480 UNUSED(type); 481 482 CHECK(cfg_getstringtoken(pctx)); 483 return (create_string(pctx, 484 TOKEN_STRING(pctx), 485 &cfg_type_sstring, 486 ret)); 487 cleanup: 488 return (result); 489 } 490 491 static void 492 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) { 493 UNUSED(pctx); 494 free(obj->value.string.base); 495 } 496 497 const char * 498 cfg_obj_asstring(const cfg_obj_t *obj) { 499 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string); 500 return (obj->value.string.base); 501 } 502 503 /* Quoted string only */ 504 cfg_type_t cfg_type_qstring = { 505 "quoted_string", cfg_parse_qstring, &cfg_rep_string, NULL 506 }; 507 508 /* Any string (quoted or unquoted); printed with quotes */ 509 cfg_type_t cfg_type_astring = { 510 "string", cfg_parse_astring, &cfg_rep_string, NULL 511 }; 512 513 /* 514 * Any string (quoted or unquoted); printed with quotes. 515 * If CFG_PRINTER_XKEY is set when printing the string will be '?' out. 516 */ 517 cfg_type_t cfg_type_sstring = { 518 "string", cfg_parse_sstring, &cfg_rep_string, NULL 519 }; 520 521 /* 522 * Booleans 523 */ 524 525 /* 526 * Lists. 527 */ 528 529 static isc_result_t 530 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) { 531 isc_result_t result; 532 533 REQUIRE(pctx != NULL); 534 REQUIRE(type != NULL); 535 REQUIRE(obj != NULL && *obj == NULL); 536 537 CHECK(cfg_create_obj(pctx, type, obj)); 538 ISC_LIST_INIT((*obj)->value.list); 539 cleanup: 540 return (result); 541 } 542 543 static isc_result_t 544 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) { 545 UNUSED(pctx); 546 cfg_listelt_t *elt; 547 548 elt = malloc(sizeof(*elt)); 549 if (elt == NULL) 550 return (ISC_R_NOMEMORY); 551 elt->obj = NULL; 552 ISC_LINK_INIT(elt, link); 553 *eltp = elt; 554 return (ISC_R_SUCCESS); 555 } 556 557 static void 558 free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) { 559 cfg_obj_destroy(pctx, &elt->obj); 560 free(elt); 561 } 562 563 static void 564 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) { 565 cfg_listelt_t *elt, *next; 566 for (elt = ISC_LIST_HEAD(obj->value.list); 567 elt != NULL; 568 elt = next) 569 { 570 next = ISC_LIST_NEXT(elt, link); 571 free_list_elt(pctx, elt); 572 } 573 } 574 575 static isc_result_t 576 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype, 577 cfg_listelt_t **ret) 578 { 579 isc_result_t result; 580 cfg_listelt_t *elt = NULL; 581 cfg_obj_t *value = NULL; 582 583 REQUIRE(pctx != NULL); 584 REQUIRE(elttype != NULL); 585 REQUIRE(ret != NULL && *ret == NULL); 586 587 CHECK(create_listelt(pctx, &elt)); 588 589 result = cfg_parse_obj(pctx, elttype, &value); 590 if (result != ISC_R_SUCCESS) 591 goto cleanup; 592 593 elt->obj = value; 594 595 *ret = elt; 596 return (ISC_R_SUCCESS); 597 598 cleanup: 599 free(elt); 600 return (result); 601 } 602 603 isc_boolean_t 604 cfg_obj_islist(const cfg_obj_t *obj) { 605 REQUIRE(obj != NULL); 606 return (ISC_TF(obj->type->rep == &cfg_rep_list)); 607 } 608 609 const cfg_listelt_t * 610 cfg_list_first(const cfg_obj_t *obj) { 611 REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list); 612 if (obj == NULL) 613 return (NULL); 614 return (ISC_LIST_HEAD(obj->value.list)); 615 } 616 617 const cfg_listelt_t * 618 cfg_list_next(const cfg_listelt_t *elt) { 619 REQUIRE(elt != NULL); 620 return (ISC_LIST_NEXT(elt, link)); 621 } 622 623 /* 624 * Return the length of a list object. If obj is NULL or is not 625 * a list, return 0. 626 */ 627 unsigned int 628 cfg_list_length(const cfg_obj_t *obj, isc_boolean_t recurse) { 629 const cfg_listelt_t *elt; 630 unsigned int count = 0; 631 632 if (obj == NULL || !cfg_obj_islist(obj)) 633 return (0U); 634 for (elt = cfg_list_first(obj); 635 elt != NULL; 636 elt = cfg_list_next(elt)) { 637 if (recurse && cfg_obj_islist(elt->obj)) { 638 count += cfg_list_length(elt->obj, recurse); 639 } else { 640 count++; 641 } 642 } 643 return (count); 644 } 645 646 /* 647 * Maps. 648 */ 649 650 /* 651 * Parse a map body. That's something like 652 * 653 * "foo 1; bar { glub; }; zap true; zap false;" 654 * 655 * i.e., a sequence of option names followed by values and 656 * terminated by semicolons. Used for the top level of 657 * the named.conf syntax, as well as for the body of the 658 * options, view, zone, and other statements. 659 */ 660 isc_result_t 661 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) 662 { 663 const cfg_clausedef_t * const *clausesets = type->of; 664 isc_result_t result; 665 const cfg_clausedef_t * const *clauseset; 666 const cfg_clausedef_t *clause; 667 cfg_obj_t *value = NULL; 668 cfg_obj_t *obj = NULL; 669 cfg_obj_t *eltobj = NULL; 670 cfg_obj_t *includename = NULL; 671 isc_symvalue_t symval; 672 673 REQUIRE(pctx != NULL); 674 REQUIRE(type != NULL); 675 REQUIRE(ret != NULL && *ret == NULL); 676 677 CHECK(create_map(pctx, type, &obj)); 678 679 obj->value.map.clausesets = clausesets; 680 681 for (;;) { 682 redo: 683 /* 684 * Parse the option name and see if it is known. 685 */ 686 CHECK(cfg_gettoken(pctx, 0)); 687 688 if (pctx->token.type != isc_tokentype_string) { 689 cfg_ungettoken(pctx); 690 break; 691 } 692 693 /* 694 * We accept "include" statements wherever a map body 695 * clause can occur. 696 */ 697 if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) { 698 /* 699 * Turn the file name into a temporary configuration 700 * object just so that it is not overwritten by the 701 * semicolon token. 702 */ 703 CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename)); 704 CHECK(parse_semicolon(pctx)); 705 CHECK(parser_openfile(pctx, includename-> 706 value.string.base)); 707 cfg_obj_destroy(pctx, &includename); 708 goto redo; 709 } 710 711 clause = NULL; 712 for (clauseset = clausesets; *clauseset != NULL; clauseset++) { 713 for (clause = *clauseset; 714 clause->name != NULL; 715 clause++) { 716 if (strcasecmp(TOKEN_STRING(pctx), 717 clause->name) == 0) 718 goto done; 719 } 720 } 721 done: 722 if (clause == NULL || clause->name == NULL) { 723 cfg_parser_error(pctx, CFG_LOG_NOPREP, 724 "unknown option"); 725 /* 726 * Try to recover by parsing this option as an unknown 727 * option and discarding it. 728 */ 729 CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, 730 &eltobj)); 731 cfg_obj_destroy(pctx, &eltobj); 732 CHECK(parse_semicolon(pctx)); 733 continue; 734 } 735 736 /* Clause is known. */ 737 738 /* See if the clause already has a value; if not create one. */ 739 result = isc_symtab_lookup(obj->value.map.symtab, 740 clause->name, 0, &symval); 741 742 /* Single-valued clause */ 743 if (result == ISC_R_NOTFOUND) { 744 CHECK(parse_symtab_elt(pctx, clause->name, 745 clause->type, 746 obj->value.map.symtab)); 747 CHECK(parse_semicolon(pctx)); 748 } else if (result == ISC_R_SUCCESS) { 749 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined", 750 clause->name); 751 result = ISC_R_EXISTS; 752 goto cleanup; 753 } else { 754 cfg_parser_error(pctx, CFG_LOG_NEAR, 755 "isc_symtab_define() failed"); 756 goto cleanup; 757 } 758 } 759 760 *ret = obj; 761 return (ISC_R_SUCCESS); 762 763 cleanup: 764 CLEANUP_OBJ(value); 765 CLEANUP_OBJ(obj); 766 CLEANUP_OBJ(eltobj); 767 CLEANUP_OBJ(includename); 768 return (result); 769 } 770 771 static isc_result_t 772 parse_symtab_elt(cfg_parser_t *pctx, const char *name, 773 cfg_type_t *elttype, isc_symtab_t *symtab) 774 { 775 isc_result_t result; 776 cfg_obj_t *obj = NULL; 777 isc_symvalue_t symval; 778 779 CHECK(cfg_parse_obj(pctx, elttype, &obj)); 780 781 symval.as_pointer = obj; 782 CHECK(isc_symtab_define(symtab, name, 783 1, symval, 784 isc_symexists_reject)); 785 return (ISC_R_SUCCESS); 786 787 cleanup: 788 CLEANUP_OBJ(obj); 789 return (result); 790 } 791 792 /* 793 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }" 794 */ 795 static isc_result_t 796 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 797 isc_result_t result; 798 799 REQUIRE(pctx != NULL); 800 REQUIRE(type != NULL); 801 REQUIRE(ret != NULL && *ret == NULL); 802 803 CHECK(cfg_parse_special(pctx, '{')); 804 CHECK(cfg_parse_mapbody(pctx, type, ret)); 805 CHECK(cfg_parse_special(pctx, '}')); 806 cleanup: 807 return (result); 808 } 809 810 /* 811 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map(). 812 */ 813 static isc_result_t 814 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, 815 const cfg_type_t *type, cfg_obj_t **ret) 816 { 817 isc_result_t result; 818 cfg_obj_t *idobj = NULL; 819 cfg_obj_t *mapobj = NULL; 820 821 REQUIRE(pctx != NULL); 822 REQUIRE(nametype != NULL); 823 REQUIRE(type != NULL); 824 REQUIRE(ret != NULL && *ret == NULL); 825 826 CHECK(cfg_parse_obj(pctx, nametype, &idobj)); 827 CHECK(cfg_parse_map(pctx, type, &mapobj)); 828 mapobj->value.map.id = idobj; 829 *ret = mapobj; 830 return (result); 831 cleanup: 832 CLEANUP_OBJ(idobj); 833 CLEANUP_OBJ(mapobj); 834 return (result); 835 } 836 837 /* 838 * Parse a map identified by a string name. E.g., "name { foo 1; }". 839 * Used for the "key" and "channel" statements. 840 */ 841 isc_result_t 842 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 843 return (parse_any_named_map(pctx, &cfg_type_astring, type, ret)); 844 } 845 846 isc_result_t 847 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) { 848 isc_result_t result; 849 isc_symvalue_t val; 850 const cfg_map_t *map; 851 852 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 853 REQUIRE(name != NULL); 854 REQUIRE(obj != NULL && *obj == NULL); 855 856 map = &mapobj->value.map; 857 858 result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val); 859 if (result != ISC_R_SUCCESS) 860 return (result); 861 *obj = val.as_pointer; 862 return (ISC_R_SUCCESS); 863 } 864 865 const cfg_obj_t * 866 cfg_map_getname(const cfg_obj_t *mapobj) { 867 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 868 return (mapobj->value.map.id); 869 } 870 871 /* Parse an arbitrary token, storing its raw text representation. */ 872 static isc_result_t 873 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 874 cfg_obj_t *obj = NULL; 875 isc_result_t result; 876 isc_region_t r; 877 878 UNUSED(type); 879 880 CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj)); 881 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); 882 if (pctx->token.type == isc_tokentype_eof) { 883 cfg_ungettoken(pctx); 884 result = ISC_R_EOF; 885 goto cleanup; 886 } 887 888 isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r); 889 890 obj->value.string.base = malloc(r.length + 1); 891 if (obj->value.string.base == NULL) { 892 result = ISC_R_NOMEMORY; 893 goto cleanup; 894 } 895 obj->value.string.length = r.length; 896 memmove(obj->value.string.base, r.base, r.length); 897 obj->value.string.base[r.length] = '\0'; 898 *ret = obj; 899 return (result); 900 901 cleanup: 902 if (obj != NULL) 903 free(obj); 904 return (result); 905 } 906 907 cfg_type_t cfg_type_token = { 908 "token", parse_token, &cfg_rep_string, NULL 909 }; 910 911 /* 912 * An unsupported option. This is just a list of tokens with balanced braces 913 * ending in a semicolon. 914 */ 915 916 static isc_result_t 917 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 918 cfg_obj_t *listobj = NULL; 919 isc_result_t result; 920 int braces = 0; 921 922 CHECK(cfg_create_list(pctx, type, &listobj)); 923 924 for (;;) { 925 cfg_listelt_t *elt = NULL; 926 927 CHECK(cfg_peektoken(pctx, 0)); 928 if (pctx->token.type == isc_tokentype_special) { 929 if (pctx->token.value.as_char == '{') 930 braces++; 931 else if (pctx->token.value.as_char == '}') 932 braces--; 933 else if (pctx->token.value.as_char == ';') 934 if (braces == 0) 935 break; 936 } 937 if (pctx->token.type == isc_tokentype_eof || braces < 0) { 938 cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token"); 939 result = ISC_R_UNEXPECTEDTOKEN; 940 goto cleanup; 941 } 942 943 CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt)); 944 ISC_LIST_APPEND(listobj->value.list, elt, link); 945 } 946 INSIST(braces == 0); 947 *ret = listobj; 948 return (ISC_R_SUCCESS); 949 950 cleanup: 951 CLEANUP_OBJ(listobj); 952 return (result); 953 } 954 955 cfg_type_t cfg_type_unsupported = { 956 "unsupported", parse_unsupported, &cfg_rep_list, NULL 957 }; 958 959 static isc_result_t 960 cfg_gettoken(cfg_parser_t *pctx, int options) { 961 isc_result_t result; 962 963 REQUIRE(pctx != NULL); 964 965 if (pctx->seen_eof) 966 return (ISC_R_SUCCESS); 967 968 options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE); 969 970 redo: 971 pctx->token.type = isc_tokentype_unknown; 972 result = isc_lex_gettoken(pctx->lexer, options, &pctx->token); 973 pctx->ungotten = ISC_FALSE; 974 pctx->line = isc_lex_getsourceline(pctx->lexer); 975 976 switch (result) { 977 case ISC_R_SUCCESS: 978 if (pctx->token.type == isc_tokentype_eof) { 979 result = isc_lex_close(pctx->lexer); 980 INSIST(result == ISC_R_NOMORE || 981 result == ISC_R_SUCCESS); 982 983 if (isc_lex_getsourcename(pctx->lexer) != NULL) { 984 /* 985 * Closed an included file, not the main file. 986 */ 987 cfg_listelt_t *elt; 988 elt = ISC_LIST_TAIL(pctx->open_files-> 989 value.list); 990 INSIST(elt != NULL); 991 ISC_LIST_UNLINK(pctx->open_files-> 992 value.list, elt, link); 993 ISC_LIST_APPEND(pctx->closed_files-> 994 value.list, elt, link); 995 goto redo; 996 } 997 pctx->seen_eof = ISC_TRUE; 998 } 999 break; 1000 1001 case ISC_R_NOSPACE: 1002 /* More understandable than "ran out of space". */ 1003 cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big"); 1004 break; 1005 1006 case ISC_R_IOERROR: 1007 cfg_parser_error(pctx, 0, "%s", 1008 isc_result_totext(result)); 1009 break; 1010 1011 default: 1012 cfg_parser_error(pctx, CFG_LOG_NEAR, "%s", 1013 isc_result_totext(result)); 1014 break; 1015 } 1016 return (result); 1017 } 1018 1019 static void 1020 cfg_ungettoken(cfg_parser_t *pctx) { 1021 REQUIRE(pctx != NULL); 1022 1023 if (pctx->seen_eof) 1024 return; 1025 isc_lex_ungettoken(pctx->lexer, &pctx->token); 1026 pctx->ungotten = ISC_TRUE; 1027 } 1028 1029 static isc_result_t 1030 cfg_peektoken(cfg_parser_t *pctx, int options) { 1031 isc_result_t result; 1032 1033 REQUIRE(pctx != NULL); 1034 1035 CHECK(cfg_gettoken(pctx, options)); 1036 cfg_ungettoken(pctx); 1037 cleanup: 1038 return (result); 1039 } 1040 1041 /* 1042 * Get a string token, accepting both the quoted and the unquoted form. 1043 * Log an error if the next token is not a string. 1044 */ 1045 static isc_result_t 1046 cfg_getstringtoken(cfg_parser_t *pctx) { 1047 isc_result_t result; 1048 1049 result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING); 1050 if (result != ISC_R_SUCCESS) 1051 return (result); 1052 1053 if (pctx->token.type != isc_tokentype_string && 1054 pctx->token.type != isc_tokentype_qstring) { 1055 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string"); 1056 return (ISC_R_UNEXPECTEDTOKEN); 1057 } 1058 return (ISC_R_SUCCESS); 1059 } 1060 1061 static void 1062 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) { 1063 va_list args; 1064 1065 REQUIRE(pctx != NULL); 1066 REQUIRE(fmt != NULL); 1067 1068 va_start(args, fmt); 1069 parser_complain(pctx, ISC_FALSE, flags, fmt, args); 1070 va_end(args); 1071 pctx->errors++; 1072 } 1073 1074 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */ 1075 1076 static isc_boolean_t 1077 have_current_file(cfg_parser_t *pctx) { 1078 cfg_listelt_t *elt; 1079 if (pctx->open_files == NULL) 1080 return (ISC_FALSE); 1081 1082 elt = ISC_LIST_TAIL(pctx->open_files->value.list); 1083 if (elt == NULL) 1084 return (ISC_FALSE); 1085 1086 return (ISC_TRUE); 1087 } 1088 1089 static char * 1090 current_file(cfg_parser_t *pctx) { 1091 static char none[] = "none"; 1092 cfg_listelt_t *elt; 1093 cfg_obj_t *fileobj; 1094 1095 if (!have_current_file(pctx)) 1096 return (none); 1097 1098 elt = ISC_LIST_TAIL(pctx->open_files->value.list); 1099 if (elt == NULL) /* shouldn't be possible, but... */ 1100 return (none); 1101 1102 fileobj = elt->obj; 1103 INSIST(fileobj->type == &cfg_type_qstring); 1104 return (fileobj->value.string.base); 1105 } 1106 1107 static void 1108 parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning, 1109 unsigned int flags, const char *format, 1110 va_list args) 1111 { 1112 char tokenbuf[MAX_LOG_TOKEN + 10]; 1113 static char where[PATH_MAX + 100]; 1114 static char message[2048]; 1115 int level = ISC_LOG_ERROR; 1116 const char *prep = ""; 1117 size_t len; 1118 1119 if (is_warning) 1120 level = ISC_LOG_WARNING; 1121 1122 where[0] = '\0'; 1123 if (have_current_file(pctx)) 1124 snprintf(where, sizeof(where), "%s:%u: ", 1125 current_file(pctx), pctx->line); 1126 1127 len = vsnprintf(message, sizeof(message), format, args); 1128 #define ELIPSIS " ... " 1129 if (len >= sizeof(message)) { 1130 message[sizeof(message) - sizeof(ELIPSIS)] = 0; 1131 strlcat(message, ELIPSIS, sizeof(message)); 1132 } 1133 1134 if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) { 1135 isc_region_t r; 1136 1137 if (pctx->ungotten) 1138 (void)cfg_gettoken(pctx, 0); 1139 1140 if (pctx->token.type == isc_tokentype_eof) { 1141 snprintf(tokenbuf, sizeof(tokenbuf), "end of file"); 1142 } else if (pctx->token.type == isc_tokentype_unknown) { 1143 flags = 0; 1144 tokenbuf[0] = '\0'; 1145 } else { 1146 isc_lex_getlasttokentext(pctx->lexer, 1147 &pctx->token, &r); 1148 if (r.length > MAX_LOG_TOKEN) 1149 snprintf(tokenbuf, sizeof(tokenbuf), 1150 "'%.*s...'", MAX_LOG_TOKEN, r.base); 1151 else 1152 snprintf(tokenbuf, sizeof(tokenbuf), 1153 "'%.*s'", (int)r.length, r.base); 1154 } 1155 1156 /* Choose a preposition. */ 1157 if (flags & CFG_LOG_NEAR) 1158 prep = " near "; 1159 else if (flags & CFG_LOG_BEFORE) 1160 prep = " before "; 1161 else 1162 prep = " "; 1163 } else { 1164 tokenbuf[0] = '\0'; 1165 } 1166 isc_log_write(pctx->lctx, CAT, MOD, level, 1167 "%s%s%s%s", where, message, prep, tokenbuf); 1168 } 1169 1170 static isc_result_t 1171 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1172 isc_result_t result; 1173 cfg_obj_t *obj; 1174 1175 REQUIRE(pctx != NULL); 1176 REQUIRE(type != NULL); 1177 REQUIRE(ret != NULL && *ret == NULL); 1178 1179 obj = malloc(sizeof(cfg_obj_t)); 1180 if (obj == NULL) 1181 return (ISC_R_NOMEMORY); 1182 obj->type = type; 1183 obj->file = current_file(pctx); 1184 obj->line = pctx->line; 1185 result = isc_refcount_init(&obj->references, 1); 1186 if (result != ISC_R_SUCCESS) { 1187 free(obj); 1188 return (result); 1189 } 1190 *ret = obj; 1191 return (ISC_R_SUCCESS); 1192 } 1193 1194 static void 1195 map_symtabitem_destroy(char *key, unsigned int type, 1196 isc_symvalue_t symval, void *userarg) 1197 { 1198 cfg_obj_t *obj = symval.as_pointer; 1199 cfg_parser_t *pctx = (cfg_parser_t *)userarg; 1200 1201 UNUSED(key); 1202 UNUSED(type); 1203 1204 cfg_obj_destroy(pctx, &obj); 1205 } 1206 1207 static isc_result_t 1208 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1209 isc_result_t result; 1210 isc_symtab_t *symtab = NULL; 1211 cfg_obj_t *obj = NULL; 1212 1213 CHECK(cfg_create_obj(pctx, type, &obj)); 1214 CHECK(isc_symtab_create(5, /* XXX */ 1215 map_symtabitem_destroy, 1216 pctx, ISC_FALSE, &symtab)); 1217 obj->value.map.symtab = symtab; 1218 obj->value.map.id = NULL; 1219 1220 *ret = obj; 1221 return (ISC_R_SUCCESS); 1222 1223 cleanup: 1224 if (obj != NULL) 1225 free(obj); 1226 return (result); 1227 } 1228 1229 static void 1230 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) { 1231 CLEANUP_OBJ(obj->value.map.id); 1232 isc_symtab_destroy(&obj->value.map.symtab); 1233 } 1234 1235 /* 1236 * Destroy 'obj', a configuration object created in 'pctx'. 1237 */ 1238 void 1239 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) { 1240 cfg_obj_t *obj; 1241 unsigned int refs; 1242 1243 REQUIRE(objp != NULL && *objp != NULL); 1244 REQUIRE(pctx != NULL); 1245 1246 obj = *objp; 1247 1248 isc_refcount_decrement(&obj->references, &refs); 1249 if (refs == 0) { 1250 obj->type->rep->free(pctx, obj); 1251 isc_refcount_destroy(&obj->references); 1252 free(obj); 1253 } 1254 *objp = NULL; 1255 } 1256