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, int 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 pctx->lctx = lctx; 255 pctx->lexer = NULL; 256 pctx->seen_eof = 0; 257 pctx->ungotten = 0; 258 pctx->errors = 0; 259 pctx->open_files = NULL; 260 pctx->closed_files = NULL; 261 pctx->line = 0; 262 pctx->token.type = isc_tokentype_unknown; 263 pctx->flags = 0; 264 265 memset(specials, 0, sizeof(specials)); 266 specials['{'] = 1; 267 specials['}'] = 1; 268 specials[';'] = 1; 269 specials['/'] = 1; 270 specials['"'] = 1; 271 specials['!'] = 1; 272 273 CHECK(isc_lex_create(1024, &pctx->lexer)); 274 275 isc_lex_setspecials(pctx->lexer, specials); 276 isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C | 277 ISC_LEXCOMMENT_CPLUSPLUS | 278 ISC_LEXCOMMENT_SHELL)); 279 280 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files)); 281 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files)); 282 283 *ret = pctx; 284 return (ISC_R_SUCCESS); 285 286 cleanup: 287 if (pctx->lexer != NULL) 288 isc_lex_destroy(&pctx->lexer); 289 CLEANUP_OBJ(pctx->open_files); 290 CLEANUP_OBJ(pctx->closed_files); 291 free(pctx); 292 return (result); 293 } 294 295 static isc_result_t 296 parser_openfile(cfg_parser_t *pctx, const char *filename) { 297 isc_result_t result; 298 cfg_listelt_t *elt = NULL; 299 cfg_obj_t *stringobj = NULL; 300 301 result = isc_lex_openfile(pctx->lexer, filename); 302 if (result != ISC_R_SUCCESS) { 303 cfg_parser_error(pctx, 0, "open: %s: %s", 304 filename, isc_result_totext(result)); 305 goto cleanup; 306 } 307 308 CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj)); 309 CHECK(create_listelt(pctx, &elt)); 310 elt->obj = stringobj; 311 ISC_LIST_APPEND(pctx->open_files->value.list, elt, link); 312 313 return (ISC_R_SUCCESS); 314 cleanup: 315 CLEANUP_OBJ(stringobj); 316 return (result); 317 } 318 319 /* 320 * Parse a configuration using a pctx where a lexer has already 321 * been set up with a source. 322 */ 323 static isc_result_t 324 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 325 isc_result_t result; 326 cfg_obj_t *obj = NULL; 327 328 result = cfg_parse_obj(pctx, type, &obj); 329 330 if (pctx->errors != 0) { 331 /* Errors have been logged. */ 332 if (result == ISC_R_SUCCESS) 333 result = ISC_R_FAILURE; 334 goto cleanup; 335 } 336 337 if (result != ISC_R_SUCCESS) { 338 /* Parsing failed but no errors have been logged. */ 339 cfg_parser_error(pctx, 0, "parsing failed: %s", 340 isc_result_totext(result)); 341 goto cleanup; 342 } 343 344 CHECK(parse_eof(pctx)); 345 346 *ret = obj; 347 return (ISC_R_SUCCESS); 348 349 cleanup: 350 CLEANUP_OBJ(obj); 351 return (result); 352 } 353 354 isc_result_t 355 cfg_parse_file(cfg_parser_t *pctx, const char *filename, 356 const cfg_type_t *type, cfg_obj_t **ret) 357 { 358 isc_result_t result; 359 360 REQUIRE(pctx != NULL); 361 REQUIRE(filename != NULL); 362 REQUIRE(type != NULL); 363 REQUIRE(ret != NULL && *ret == NULL); 364 365 CHECK(parser_openfile(pctx, filename)); 366 CHECK(parse2(pctx, type, ret)); 367 cleanup: 368 return (result); 369 } 370 371 void 372 cfg_parser_destroy(cfg_parser_t **pctxp) { 373 cfg_parser_t *pctx; 374 375 REQUIRE(pctxp != NULL && *pctxp != NULL); 376 377 pctx = *pctxp; 378 *pctxp = NULL; 379 380 isc_lex_destroy(&pctx->lexer); 381 /* 382 * Cleaning up open_files does not 383 * close the files; that was already done 384 * by closing the lexer. 385 */ 386 CLEANUP_OBJ(pctx->open_files); 387 CLEANUP_OBJ(pctx->closed_files); 388 free(pctx); 389 } 390 391 /* 392 * qstring (quoted string), ustring (unquoted string), astring 393 * (any string) 394 */ 395 396 /* Create a string object from a null-terminated C string. */ 397 static isc_result_t 398 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type, 399 cfg_obj_t **ret) 400 { 401 isc_result_t result; 402 cfg_obj_t *obj = NULL; 403 int len; 404 405 CHECK(cfg_create_obj(pctx, type, &obj)); 406 len = strlen(contents); 407 obj->value.string.length = len; 408 obj->value.string.base = malloc(len + 1); 409 if (obj->value.string.base == NULL) { 410 free(obj); 411 return (ISC_R_NOMEMORY); 412 } 413 memmove(obj->value.string.base, contents, len); 414 obj->value.string.base[len] = '\0'; 415 416 *ret = obj; 417 cleanup: 418 return (result); 419 } 420 421 static isc_result_t 422 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 423 isc_result_t result; 424 425 REQUIRE(pctx != NULL); 426 REQUIRE(ret != NULL && *ret == NULL); 427 428 UNUSED(type); 429 430 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); 431 if (pctx->token.type != isc_tokentype_qstring) { 432 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string"); 433 return (ISC_R_UNEXPECTEDTOKEN); 434 } 435 return (create_string(pctx, TOKEN_STRING(pctx), 436 &cfg_type_qstring, ret)); 437 cleanup: 438 return (result); 439 } 440 441 static isc_result_t 442 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, 443 cfg_obj_t **ret) 444 { 445 isc_result_t result; 446 447 REQUIRE(pctx != NULL); 448 REQUIRE(ret != NULL && *ret == NULL); 449 450 UNUSED(type); 451 452 CHECK(cfg_getstringtoken(pctx)); 453 return (create_string(pctx, 454 TOKEN_STRING(pctx), 455 &cfg_type_qstring, 456 ret)); 457 cleanup: 458 return (result); 459 } 460 461 static isc_result_t 462 cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type, 463 cfg_obj_t **ret) 464 { 465 isc_result_t result; 466 467 REQUIRE(pctx != NULL); 468 REQUIRE(ret != NULL && *ret == NULL); 469 470 UNUSED(type); 471 472 CHECK(cfg_getstringtoken(pctx)); 473 return (create_string(pctx, 474 TOKEN_STRING(pctx), 475 &cfg_type_sstring, 476 ret)); 477 cleanup: 478 return (result); 479 } 480 481 static void 482 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) { 483 UNUSED(pctx); 484 free(obj->value.string.base); 485 } 486 487 const char * 488 cfg_obj_asstring(const cfg_obj_t *obj) { 489 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string); 490 return (obj->value.string.base); 491 } 492 493 /* Quoted string only */ 494 cfg_type_t cfg_type_qstring = { 495 "quoted_string", cfg_parse_qstring, &cfg_rep_string, NULL 496 }; 497 498 /* Any string (quoted or unquoted); printed with quotes */ 499 cfg_type_t cfg_type_astring = { 500 "string", cfg_parse_astring, &cfg_rep_string, NULL 501 }; 502 503 /* 504 * Any string (quoted or unquoted); printed with quotes. 505 * If CFG_PRINTER_XKEY is set when printing the string will be '?' out. 506 */ 507 cfg_type_t cfg_type_sstring = { 508 "string", cfg_parse_sstring, &cfg_rep_string, NULL 509 }; 510 511 /* 512 * Booleans 513 */ 514 515 /* 516 * Lists. 517 */ 518 519 static isc_result_t 520 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) { 521 isc_result_t result; 522 523 REQUIRE(pctx != NULL); 524 REQUIRE(type != NULL); 525 REQUIRE(obj != NULL && *obj == NULL); 526 527 CHECK(cfg_create_obj(pctx, type, obj)); 528 ISC_LIST_INIT((*obj)->value.list); 529 cleanup: 530 return (result); 531 } 532 533 static isc_result_t 534 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) { 535 UNUSED(pctx); 536 cfg_listelt_t *elt; 537 538 elt = malloc(sizeof(*elt)); 539 if (elt == NULL) 540 return (ISC_R_NOMEMORY); 541 elt->obj = NULL; 542 ISC_LINK_INIT(elt, link); 543 *eltp = elt; 544 return (ISC_R_SUCCESS); 545 } 546 547 static void 548 free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) { 549 cfg_obj_destroy(pctx, &elt->obj); 550 free(elt); 551 } 552 553 static void 554 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) { 555 cfg_listelt_t *elt, *next; 556 for (elt = ISC_LIST_HEAD(obj->value.list); 557 elt != NULL; 558 elt = next) 559 { 560 next = ISC_LIST_NEXT(elt, link); 561 free_list_elt(pctx, elt); 562 } 563 } 564 565 static isc_result_t 566 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype, 567 cfg_listelt_t **ret) 568 { 569 isc_result_t result; 570 cfg_listelt_t *elt = NULL; 571 cfg_obj_t *value = NULL; 572 573 REQUIRE(pctx != NULL); 574 REQUIRE(elttype != NULL); 575 REQUIRE(ret != NULL && *ret == NULL); 576 577 CHECK(create_listelt(pctx, &elt)); 578 579 result = cfg_parse_obj(pctx, elttype, &value); 580 if (result != ISC_R_SUCCESS) 581 goto cleanup; 582 583 elt->obj = value; 584 585 *ret = elt; 586 return (ISC_R_SUCCESS); 587 588 cleanup: 589 free(elt); 590 return (result); 591 } 592 593 int 594 cfg_obj_islist(const cfg_obj_t *obj) { 595 REQUIRE(obj != NULL); 596 return (obj->type->rep == &cfg_rep_list); 597 } 598 599 const cfg_listelt_t * 600 cfg_list_first(const cfg_obj_t *obj) { 601 REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list); 602 if (obj == NULL) 603 return (NULL); 604 return (ISC_LIST_HEAD(obj->value.list)); 605 } 606 607 const cfg_listelt_t * 608 cfg_list_next(const cfg_listelt_t *elt) { 609 REQUIRE(elt != NULL); 610 return (ISC_LIST_NEXT(elt, link)); 611 } 612 613 /* 614 * Return the length of a list object. If obj is NULL or is not 615 * a list, return 0. 616 */ 617 unsigned int 618 cfg_list_length(const cfg_obj_t *obj, int recurse) { 619 const cfg_listelt_t *elt; 620 unsigned int count = 0; 621 622 if (obj == NULL || !cfg_obj_islist(obj)) 623 return (0U); 624 for (elt = cfg_list_first(obj); 625 elt != NULL; 626 elt = cfg_list_next(elt)) { 627 if (recurse && cfg_obj_islist(elt->obj)) { 628 count += cfg_list_length(elt->obj, recurse); 629 } else { 630 count++; 631 } 632 } 633 return (count); 634 } 635 636 /* 637 * Maps. 638 */ 639 640 /* 641 * Parse a map body. That's something like 642 * 643 * "foo 1; bar { glub; }; zap true; zap false;" 644 * 645 * i.e., a sequence of option names followed by values and 646 * terminated by semicolons. Used for the top level of 647 * the named.conf syntax, as well as for the body of the 648 * options, view, zone, and other statements. 649 */ 650 isc_result_t 651 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) 652 { 653 const cfg_clausedef_t * const *clausesets = type->of; 654 isc_result_t result; 655 const cfg_clausedef_t * const *clauseset; 656 const cfg_clausedef_t *clause; 657 cfg_obj_t *value = NULL; 658 cfg_obj_t *obj = NULL; 659 cfg_obj_t *eltobj = NULL; 660 cfg_obj_t *includename = NULL; 661 isc_symvalue_t symval; 662 663 REQUIRE(pctx != NULL); 664 REQUIRE(type != NULL); 665 REQUIRE(ret != NULL && *ret == NULL); 666 667 CHECK(create_map(pctx, type, &obj)); 668 669 obj->value.map.clausesets = clausesets; 670 671 for (;;) { 672 redo: 673 /* 674 * Parse the option name and see if it is known. 675 */ 676 CHECK(cfg_gettoken(pctx, 0)); 677 678 if (pctx->token.type != isc_tokentype_string) { 679 cfg_ungettoken(pctx); 680 break; 681 } 682 683 /* 684 * We accept "include" statements wherever a map body 685 * clause can occur. 686 */ 687 if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) { 688 /* 689 * Turn the file name into a temporary configuration 690 * object just so that it is not overwritten by the 691 * semicolon token. 692 */ 693 CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename)); 694 CHECK(parse_semicolon(pctx)); 695 CHECK(parser_openfile(pctx, includename-> 696 value.string.base)); 697 cfg_obj_destroy(pctx, &includename); 698 goto redo; 699 } 700 701 clause = NULL; 702 for (clauseset = clausesets; *clauseset != NULL; clauseset++) { 703 for (clause = *clauseset; 704 clause->name != NULL; 705 clause++) { 706 if (strcasecmp(TOKEN_STRING(pctx), 707 clause->name) == 0) 708 goto done; 709 } 710 } 711 done: 712 if (clause == NULL || clause->name == NULL) { 713 cfg_parser_error(pctx, CFG_LOG_NOPREP, 714 "unknown option"); 715 /* 716 * Try to recover by parsing this option as an unknown 717 * option and discarding it. 718 */ 719 CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, 720 &eltobj)); 721 cfg_obj_destroy(pctx, &eltobj); 722 CHECK(parse_semicolon(pctx)); 723 continue; 724 } 725 726 /* Clause is known. */ 727 728 /* See if the clause already has a value; if not create one. */ 729 result = isc_symtab_lookup(obj->value.map.symtab, 730 clause->name, 0, &symval); 731 732 /* Single-valued clause */ 733 if (result == ISC_R_NOTFOUND) { 734 CHECK(parse_symtab_elt(pctx, clause->name, 735 clause->type, 736 obj->value.map.symtab)); 737 CHECK(parse_semicolon(pctx)); 738 } else if (result == ISC_R_SUCCESS) { 739 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined", 740 clause->name); 741 result = ISC_R_EXISTS; 742 goto cleanup; 743 } else { 744 cfg_parser_error(pctx, CFG_LOG_NEAR, 745 "isc_symtab_define() failed"); 746 goto cleanup; 747 } 748 } 749 750 *ret = obj; 751 return (ISC_R_SUCCESS); 752 753 cleanup: 754 CLEANUP_OBJ(value); 755 CLEANUP_OBJ(obj); 756 CLEANUP_OBJ(eltobj); 757 CLEANUP_OBJ(includename); 758 return (result); 759 } 760 761 static isc_result_t 762 parse_symtab_elt(cfg_parser_t *pctx, const char *name, 763 cfg_type_t *elttype, isc_symtab_t *symtab) 764 { 765 isc_result_t result; 766 cfg_obj_t *obj = NULL; 767 isc_symvalue_t symval; 768 769 CHECK(cfg_parse_obj(pctx, elttype, &obj)); 770 771 symval.as_pointer = obj; 772 CHECK(isc_symtab_define(symtab, name, 773 1, symval, 774 isc_symexists_reject)); 775 return (ISC_R_SUCCESS); 776 777 cleanup: 778 CLEANUP_OBJ(obj); 779 return (result); 780 } 781 782 /* 783 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }" 784 */ 785 static isc_result_t 786 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 787 isc_result_t result; 788 789 REQUIRE(pctx != NULL); 790 REQUIRE(type != NULL); 791 REQUIRE(ret != NULL && *ret == NULL); 792 793 CHECK(cfg_parse_special(pctx, '{')); 794 CHECK(cfg_parse_mapbody(pctx, type, ret)); 795 CHECK(cfg_parse_special(pctx, '}')); 796 cleanup: 797 return (result); 798 } 799 800 /* 801 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map(). 802 */ 803 static isc_result_t 804 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, 805 const cfg_type_t *type, cfg_obj_t **ret) 806 { 807 isc_result_t result; 808 cfg_obj_t *idobj = NULL; 809 cfg_obj_t *mapobj = NULL; 810 811 REQUIRE(pctx != NULL); 812 REQUIRE(nametype != NULL); 813 REQUIRE(type != NULL); 814 REQUIRE(ret != NULL && *ret == NULL); 815 816 CHECK(cfg_parse_obj(pctx, nametype, &idobj)); 817 CHECK(cfg_parse_map(pctx, type, &mapobj)); 818 mapobj->value.map.id = idobj; 819 *ret = mapobj; 820 return (result); 821 cleanup: 822 CLEANUP_OBJ(idobj); 823 CLEANUP_OBJ(mapobj); 824 return (result); 825 } 826 827 /* 828 * Parse a map identified by a string name. E.g., "name { foo 1; }". 829 * Used for the "key" and "channel" statements. 830 */ 831 isc_result_t 832 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 833 return (parse_any_named_map(pctx, &cfg_type_astring, type, ret)); 834 } 835 836 isc_result_t 837 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) { 838 isc_result_t result; 839 isc_symvalue_t val; 840 const cfg_map_t *map; 841 842 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 843 REQUIRE(name != NULL); 844 REQUIRE(obj != NULL && *obj == NULL); 845 846 map = &mapobj->value.map; 847 848 result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val); 849 if (result != ISC_R_SUCCESS) 850 return (result); 851 *obj = val.as_pointer; 852 return (ISC_R_SUCCESS); 853 } 854 855 const cfg_obj_t * 856 cfg_map_getname(const cfg_obj_t *mapobj) { 857 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 858 return (mapobj->value.map.id); 859 } 860 861 /* Parse an arbitrary token, storing its raw text representation. */ 862 static isc_result_t 863 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 864 cfg_obj_t *obj = NULL; 865 isc_result_t result; 866 isc_region_t r; 867 868 UNUSED(type); 869 870 CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj)); 871 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); 872 if (pctx->token.type == isc_tokentype_eof) { 873 cfg_ungettoken(pctx); 874 result = ISC_R_EOF; 875 goto cleanup; 876 } 877 878 isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r); 879 880 obj->value.string.base = malloc(r.length + 1); 881 if (obj->value.string.base == NULL) { 882 result = ISC_R_NOMEMORY; 883 goto cleanup; 884 } 885 obj->value.string.length = r.length; 886 memmove(obj->value.string.base, r.base, r.length); 887 obj->value.string.base[r.length] = '\0'; 888 *ret = obj; 889 return (result); 890 891 cleanup: 892 if (obj != NULL) 893 free(obj); 894 return (result); 895 } 896 897 cfg_type_t cfg_type_token = { 898 "token", parse_token, &cfg_rep_string, NULL 899 }; 900 901 /* 902 * An unsupported option. This is just a list of tokens with balanced braces 903 * ending in a semicolon. 904 */ 905 906 static isc_result_t 907 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 908 cfg_obj_t *listobj = NULL; 909 isc_result_t result; 910 int braces = 0; 911 912 CHECK(cfg_create_list(pctx, type, &listobj)); 913 914 for (;;) { 915 cfg_listelt_t *elt = NULL; 916 917 CHECK(cfg_peektoken(pctx, 0)); 918 if (pctx->token.type == isc_tokentype_special) { 919 if (pctx->token.value.as_char == '{') 920 braces++; 921 else if (pctx->token.value.as_char == '}') 922 braces--; 923 else if (pctx->token.value.as_char == ';') 924 if (braces == 0) 925 break; 926 } 927 if (pctx->token.type == isc_tokentype_eof || braces < 0) { 928 cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token"); 929 result = ISC_R_UNEXPECTEDTOKEN; 930 goto cleanup; 931 } 932 933 CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt)); 934 ISC_LIST_APPEND(listobj->value.list, elt, link); 935 } 936 INSIST(braces == 0); 937 *ret = listobj; 938 return (ISC_R_SUCCESS); 939 940 cleanup: 941 CLEANUP_OBJ(listobj); 942 return (result); 943 } 944 945 cfg_type_t cfg_type_unsupported = { 946 "unsupported", parse_unsupported, &cfg_rep_list, NULL 947 }; 948 949 static isc_result_t 950 cfg_gettoken(cfg_parser_t *pctx, int options) { 951 isc_result_t result; 952 953 REQUIRE(pctx != NULL); 954 955 if (pctx->seen_eof) 956 return (ISC_R_SUCCESS); 957 958 options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE); 959 960 redo: 961 pctx->token.type = isc_tokentype_unknown; 962 result = isc_lex_gettoken(pctx->lexer, options, &pctx->token); 963 pctx->ungotten = 0; 964 pctx->line = isc_lex_getsourceline(pctx->lexer); 965 966 switch (result) { 967 case ISC_R_SUCCESS: 968 if (pctx->token.type == isc_tokentype_eof) { 969 result = isc_lex_close(pctx->lexer); 970 INSIST(result == ISC_R_NOMORE || 971 result == ISC_R_SUCCESS); 972 973 if (isc_lex_getsourcename(pctx->lexer) != NULL) { 974 /* 975 * Closed an included file, not the main file. 976 */ 977 cfg_listelt_t *elt; 978 elt = ISC_LIST_TAIL(pctx->open_files-> 979 value.list); 980 INSIST(elt != NULL); 981 ISC_LIST_UNLINK(pctx->open_files-> 982 value.list, elt, link); 983 ISC_LIST_APPEND(pctx->closed_files-> 984 value.list, elt, link); 985 goto redo; 986 } 987 pctx->seen_eof = 1; 988 } 989 break; 990 991 case ISC_R_NOSPACE: 992 /* More understandable than "ran out of space". */ 993 cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big"); 994 break; 995 996 case ISC_R_IOERROR: 997 cfg_parser_error(pctx, 0, "%s", 998 isc_result_totext(result)); 999 break; 1000 1001 default: 1002 cfg_parser_error(pctx, CFG_LOG_NEAR, "%s", 1003 isc_result_totext(result)); 1004 break; 1005 } 1006 return (result); 1007 } 1008 1009 static void 1010 cfg_ungettoken(cfg_parser_t *pctx) { 1011 REQUIRE(pctx != NULL); 1012 1013 if (pctx->seen_eof) 1014 return; 1015 isc_lex_ungettoken(pctx->lexer, &pctx->token); 1016 pctx->ungotten = 1; 1017 } 1018 1019 static isc_result_t 1020 cfg_peektoken(cfg_parser_t *pctx, int options) { 1021 isc_result_t result; 1022 1023 REQUIRE(pctx != NULL); 1024 1025 CHECK(cfg_gettoken(pctx, options)); 1026 cfg_ungettoken(pctx); 1027 cleanup: 1028 return (result); 1029 } 1030 1031 /* 1032 * Get a string token, accepting both the quoted and the unquoted form. 1033 * Log an error if the next token is not a string. 1034 */ 1035 static isc_result_t 1036 cfg_getstringtoken(cfg_parser_t *pctx) { 1037 isc_result_t result; 1038 1039 result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING); 1040 if (result != ISC_R_SUCCESS) 1041 return (result); 1042 1043 if (pctx->token.type != isc_tokentype_string && 1044 pctx->token.type != isc_tokentype_qstring) { 1045 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string"); 1046 return (ISC_R_UNEXPECTEDTOKEN); 1047 } 1048 return (ISC_R_SUCCESS); 1049 } 1050 1051 static void 1052 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) { 1053 va_list args; 1054 1055 REQUIRE(pctx != NULL); 1056 REQUIRE(fmt != NULL); 1057 1058 va_start(args, fmt); 1059 parser_complain(pctx, 0, flags, fmt, args); 1060 va_end(args); 1061 pctx->errors++; 1062 } 1063 1064 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */ 1065 1066 static int 1067 have_current_file(cfg_parser_t *pctx) { 1068 cfg_listelt_t *elt; 1069 if (pctx->open_files == NULL) 1070 return (0); 1071 1072 elt = ISC_LIST_TAIL(pctx->open_files->value.list); 1073 if (elt == NULL) 1074 return (0); 1075 1076 return (1); 1077 } 1078 1079 static char * 1080 current_file(cfg_parser_t *pctx) { 1081 static char none[] = "none"; 1082 cfg_listelt_t *elt; 1083 cfg_obj_t *fileobj; 1084 1085 if (!have_current_file(pctx)) 1086 return (none); 1087 1088 elt = ISC_LIST_TAIL(pctx->open_files->value.list); 1089 if (elt == NULL) /* shouldn't be possible, but... */ 1090 return (none); 1091 1092 fileobj = elt->obj; 1093 INSIST(fileobj->type == &cfg_type_qstring); 1094 return (fileobj->value.string.base); 1095 } 1096 1097 static void 1098 parser_complain(cfg_parser_t *pctx, int is_warning, 1099 unsigned int flags, const char *format, 1100 va_list args) 1101 { 1102 char tokenbuf[MAX_LOG_TOKEN + 10]; 1103 static char where[PATH_MAX + 100]; 1104 static char message[2048]; 1105 int level = ISC_LOG_ERROR; 1106 const char *prep = ""; 1107 size_t len; 1108 1109 if (is_warning) 1110 level = ISC_LOG_WARNING; 1111 1112 where[0] = '\0'; 1113 if (have_current_file(pctx)) 1114 snprintf(where, sizeof(where), "%s:%u: ", 1115 current_file(pctx), pctx->line); 1116 1117 len = vsnprintf(message, sizeof(message), format, args); 1118 #define ELIPSIS " ... " 1119 if (len >= sizeof(message)) { 1120 message[sizeof(message) - sizeof(ELIPSIS)] = 0; 1121 strlcat(message, ELIPSIS, sizeof(message)); 1122 } 1123 1124 if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) { 1125 isc_region_t r; 1126 1127 if (pctx->ungotten) 1128 (void)cfg_gettoken(pctx, 0); 1129 1130 if (pctx->token.type == isc_tokentype_eof) { 1131 snprintf(tokenbuf, sizeof(tokenbuf), "end of file"); 1132 } else if (pctx->token.type == isc_tokentype_unknown) { 1133 flags = 0; 1134 tokenbuf[0] = '\0'; 1135 } else { 1136 isc_lex_getlasttokentext(pctx->lexer, 1137 &pctx->token, &r); 1138 if (r.length > MAX_LOG_TOKEN) 1139 snprintf(tokenbuf, sizeof(tokenbuf), 1140 "'%.*s...'", MAX_LOG_TOKEN, r.base); 1141 else 1142 snprintf(tokenbuf, sizeof(tokenbuf), 1143 "'%.*s'", (int)r.length, r.base); 1144 } 1145 1146 /* Choose a preposition. */ 1147 if (flags & CFG_LOG_NEAR) 1148 prep = " near "; 1149 else if (flags & CFG_LOG_BEFORE) 1150 prep = " before "; 1151 else 1152 prep = " "; 1153 } else { 1154 tokenbuf[0] = '\0'; 1155 } 1156 isc_log_write(pctx->lctx, CAT, MOD, level, 1157 "%s%s%s%s", where, message, prep, tokenbuf); 1158 } 1159 1160 static isc_result_t 1161 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1162 cfg_obj_t *obj; 1163 1164 REQUIRE(pctx != NULL); 1165 REQUIRE(type != NULL); 1166 REQUIRE(ret != NULL && *ret == NULL); 1167 1168 obj = malloc(sizeof(cfg_obj_t)); 1169 if (obj == NULL) 1170 return (ISC_R_NOMEMORY); 1171 obj->type = type; 1172 obj->file = current_file(pctx); 1173 obj->line = pctx->line; 1174 *ret = obj; 1175 return (ISC_R_SUCCESS); 1176 } 1177 1178 static void 1179 map_symtabitem_destroy(char *key, unsigned int type, 1180 isc_symvalue_t symval, void *userarg) 1181 { 1182 cfg_obj_t *obj = symval.as_pointer; 1183 cfg_parser_t *pctx = (cfg_parser_t *)userarg; 1184 1185 UNUSED(key); 1186 UNUSED(type); 1187 1188 cfg_obj_destroy(pctx, &obj); 1189 } 1190 1191 static isc_result_t 1192 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1193 isc_result_t result; 1194 isc_symtab_t *symtab = NULL; 1195 cfg_obj_t *obj = NULL; 1196 1197 CHECK(cfg_create_obj(pctx, type, &obj)); 1198 CHECK(isc_symtab_create(5, /* XXX */ 1199 map_symtabitem_destroy, 1200 pctx, 0, &symtab)); 1201 obj->value.map.symtab = symtab; 1202 obj->value.map.id = NULL; 1203 1204 *ret = obj; 1205 return (ISC_R_SUCCESS); 1206 1207 cleanup: 1208 if (obj != NULL) 1209 free(obj); 1210 return (result); 1211 } 1212 1213 static void 1214 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) { 1215 CLEANUP_OBJ(obj->value.map.id); 1216 isc_symtab_destroy(&obj->value.map.symtab); 1217 } 1218 1219 /* 1220 * Destroy 'obj', a configuration object created in 'pctx'. 1221 */ 1222 void 1223 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) { 1224 cfg_obj_t *obj; 1225 1226 REQUIRE(objp != NULL && *objp != NULL); 1227 REQUIRE(pctx != NULL); 1228 1229 obj = *objp; 1230 1231 obj->type->rep->free(pctx, obj); 1232 free(obj); 1233 *objp = NULL; 1234 } 1235