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 /* 594 * Maps. 595 */ 596 597 /* 598 * Parse a map body. That's something like 599 * 600 * "foo 1; bar { glub; }; zap true; zap false;" 601 * 602 * i.e., a sequence of option names followed by values and 603 * terminated by semicolons. Used for the top level of 604 * the named.conf syntax, as well as for the body of the 605 * options, view, zone, and other statements. 606 */ 607 isc_result_t 608 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) 609 { 610 const cfg_clausedef_t * const *clausesets = type->of; 611 isc_result_t result; 612 const cfg_clausedef_t * const *clauseset; 613 const cfg_clausedef_t *clause; 614 cfg_obj_t *value = NULL; 615 cfg_obj_t *obj = NULL; 616 cfg_obj_t *eltobj = NULL; 617 cfg_obj_t *includename = NULL; 618 isc_symvalue_t symval; 619 620 REQUIRE(pctx != NULL); 621 REQUIRE(type != NULL); 622 REQUIRE(ret != NULL && *ret == NULL); 623 624 CHECK(create_map(pctx, type, &obj)); 625 626 obj->value.map.clausesets = clausesets; 627 628 for (;;) { 629 redo: 630 /* 631 * Parse the option name and see if it is known. 632 */ 633 CHECK(cfg_gettoken(pctx, 0)); 634 635 if (pctx->token.type != isc_tokentype_string) { 636 cfg_ungettoken(pctx); 637 break; 638 } 639 640 /* 641 * We accept "include" statements wherever a map body 642 * clause can occur. 643 */ 644 if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) { 645 /* 646 * Turn the file name into a temporary configuration 647 * object just so that it is not overwritten by the 648 * semicolon token. 649 */ 650 CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename)); 651 CHECK(parse_semicolon(pctx)); 652 CHECK(parser_openfile(pctx, includename-> 653 value.string.base)); 654 cfg_obj_destroy(pctx, &includename); 655 goto redo; 656 } 657 658 clause = NULL; 659 for (clauseset = clausesets; *clauseset != NULL; clauseset++) { 660 for (clause = *clauseset; 661 clause->name != NULL; 662 clause++) { 663 if (strcasecmp(TOKEN_STRING(pctx), 664 clause->name) == 0) 665 goto done; 666 } 667 } 668 done: 669 if (clause == NULL || clause->name == NULL) { 670 cfg_parser_error(pctx, CFG_LOG_NOPREP, 671 "unknown option"); 672 /* 673 * Try to recover by parsing this option as an unknown 674 * option and discarding it. 675 */ 676 CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, 677 &eltobj)); 678 cfg_obj_destroy(pctx, &eltobj); 679 CHECK(parse_semicolon(pctx)); 680 continue; 681 } 682 683 /* Clause is known. */ 684 685 /* See if the clause already has a value; if not create one. */ 686 result = isc_symtab_lookup(obj->value.map.symtab, 687 clause->name, 0, &symval); 688 689 /* Single-valued clause */ 690 if (result == ISC_R_NOTFOUND) { 691 CHECK(parse_symtab_elt(pctx, clause->name, 692 clause->type, 693 obj->value.map.symtab)); 694 CHECK(parse_semicolon(pctx)); 695 } else if (result == ISC_R_SUCCESS) { 696 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined", 697 clause->name); 698 result = ISC_R_EXISTS; 699 goto cleanup; 700 } else { 701 cfg_parser_error(pctx, CFG_LOG_NEAR, 702 "isc_symtab_define() failed"); 703 goto cleanup; 704 } 705 } 706 707 *ret = obj; 708 return (ISC_R_SUCCESS); 709 710 cleanup: 711 CLEANUP_OBJ(value); 712 CLEANUP_OBJ(obj); 713 CLEANUP_OBJ(eltobj); 714 CLEANUP_OBJ(includename); 715 return (result); 716 } 717 718 static isc_result_t 719 parse_symtab_elt(cfg_parser_t *pctx, const char *name, 720 cfg_type_t *elttype, isc_symtab_t *symtab) 721 { 722 isc_result_t result; 723 cfg_obj_t *obj = NULL; 724 isc_symvalue_t symval; 725 726 CHECK(cfg_parse_obj(pctx, elttype, &obj)); 727 728 symval.as_pointer = obj; 729 CHECK(isc_symtab_define(symtab, name, 730 1, symval, 731 isc_symexists_reject)); 732 return (ISC_R_SUCCESS); 733 734 cleanup: 735 CLEANUP_OBJ(obj); 736 return (result); 737 } 738 739 /* 740 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }" 741 */ 742 static isc_result_t 743 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 744 isc_result_t result; 745 746 REQUIRE(pctx != NULL); 747 REQUIRE(type != NULL); 748 REQUIRE(ret != NULL && *ret == NULL); 749 750 CHECK(cfg_parse_special(pctx, '{')); 751 CHECK(cfg_parse_mapbody(pctx, type, ret)); 752 CHECK(cfg_parse_special(pctx, '}')); 753 cleanup: 754 return (result); 755 } 756 757 /* 758 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map(). 759 */ 760 static isc_result_t 761 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, 762 const cfg_type_t *type, cfg_obj_t **ret) 763 { 764 isc_result_t result; 765 cfg_obj_t *idobj = NULL; 766 cfg_obj_t *mapobj = NULL; 767 768 REQUIRE(pctx != NULL); 769 REQUIRE(nametype != NULL); 770 REQUIRE(type != NULL); 771 REQUIRE(ret != NULL && *ret == NULL); 772 773 CHECK(cfg_parse_obj(pctx, nametype, &idobj)); 774 CHECK(cfg_parse_map(pctx, type, &mapobj)); 775 mapobj->value.map.id = idobj; 776 *ret = mapobj; 777 return (result); 778 cleanup: 779 CLEANUP_OBJ(idobj); 780 CLEANUP_OBJ(mapobj); 781 return (result); 782 } 783 784 /* 785 * Parse a map identified by a string name. E.g., "name { foo 1; }". 786 * Used for the "key" and "channel" statements. 787 */ 788 isc_result_t 789 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 790 return (parse_any_named_map(pctx, &cfg_type_astring, type, ret)); 791 } 792 793 isc_result_t 794 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) { 795 isc_result_t result; 796 isc_symvalue_t val; 797 const cfg_map_t *map; 798 799 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 800 REQUIRE(name != NULL); 801 REQUIRE(obj != NULL && *obj == NULL); 802 803 map = &mapobj->value.map; 804 805 result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val); 806 if (result != ISC_R_SUCCESS) 807 return (result); 808 *obj = val.as_pointer; 809 return (ISC_R_SUCCESS); 810 } 811 812 const cfg_obj_t * 813 cfg_map_getname(const cfg_obj_t *mapobj) { 814 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 815 return (mapobj->value.map.id); 816 } 817 818 /* Parse an arbitrary token, storing its raw text representation. */ 819 static isc_result_t 820 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 821 cfg_obj_t *obj = NULL; 822 isc_result_t result; 823 isc_region_t r; 824 825 UNUSED(type); 826 827 CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj)); 828 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); 829 if (pctx->token.type == isc_tokentype_eof) { 830 cfg_ungettoken(pctx); 831 result = ISC_R_EOF; 832 goto cleanup; 833 } 834 835 isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r); 836 837 obj->value.string.base = malloc(r.length + 1); 838 if (obj->value.string.base == NULL) { 839 result = ISC_R_NOMEMORY; 840 goto cleanup; 841 } 842 obj->value.string.length = r.length; 843 memmove(obj->value.string.base, r.base, r.length); 844 obj->value.string.base[r.length] = '\0'; 845 *ret = obj; 846 return (result); 847 848 cleanup: 849 if (obj != NULL) 850 free(obj); 851 return (result); 852 } 853 854 cfg_type_t cfg_type_token = { 855 "token", parse_token, &cfg_rep_string, NULL 856 }; 857 858 /* 859 * An unsupported option. This is just a list of tokens with balanced braces 860 * ending in a semicolon. 861 */ 862 863 static isc_result_t 864 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 865 cfg_obj_t *listobj = NULL; 866 isc_result_t result; 867 int braces = 0; 868 869 CHECK(cfg_create_list(pctx, type, &listobj)); 870 871 for (;;) { 872 cfg_listelt_t *elt = NULL; 873 874 CHECK(cfg_peektoken(pctx, 0)); 875 if (pctx->token.type == isc_tokentype_special) { 876 if (pctx->token.value.as_char == '{') 877 braces++; 878 else if (pctx->token.value.as_char == '}') 879 braces--; 880 else if (pctx->token.value.as_char == ';') 881 if (braces == 0) 882 break; 883 } 884 if (pctx->token.type == isc_tokentype_eof || braces < 0) { 885 cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token"); 886 result = ISC_R_UNEXPECTEDTOKEN; 887 goto cleanup; 888 } 889 890 CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt)); 891 ISC_LIST_APPEND(listobj->value.list, elt, link); 892 } 893 INSIST(braces == 0); 894 *ret = listobj; 895 return (ISC_R_SUCCESS); 896 897 cleanup: 898 CLEANUP_OBJ(listobj); 899 return (result); 900 } 901 902 cfg_type_t cfg_type_unsupported = { 903 "unsupported", parse_unsupported, &cfg_rep_list, NULL 904 }; 905 906 static isc_result_t 907 cfg_gettoken(cfg_parser_t *pctx, int options) { 908 isc_result_t result; 909 910 REQUIRE(pctx != NULL); 911 912 if (pctx->seen_eof) 913 return (ISC_R_SUCCESS); 914 915 options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE); 916 917 redo: 918 pctx->token.type = isc_tokentype_unknown; 919 result = isc_lex_gettoken(pctx->lexer, options, &pctx->token); 920 pctx->ungotten = 0; 921 pctx->line = isc_lex_getsourceline(pctx->lexer); 922 923 switch (result) { 924 case ISC_R_SUCCESS: 925 if (pctx->token.type == isc_tokentype_eof) { 926 result = isc_lex_close(pctx->lexer); 927 INSIST(result == ISC_R_NOMORE || 928 result == ISC_R_SUCCESS); 929 930 if (isc_lex_getsourcename(pctx->lexer) != NULL) { 931 /* 932 * Closed an included file, not the main file. 933 */ 934 cfg_listelt_t *elt; 935 elt = ISC_LIST_TAIL(pctx->open_files-> 936 value.list); 937 INSIST(elt != NULL); 938 ISC_LIST_UNLINK(pctx->open_files-> 939 value.list, elt, link); 940 ISC_LIST_APPEND(pctx->closed_files-> 941 value.list, elt, link); 942 goto redo; 943 } 944 pctx->seen_eof = 1; 945 } 946 break; 947 948 case ISC_R_NOSPACE: 949 /* More understandable than "ran out of space". */ 950 cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big"); 951 break; 952 953 case ISC_R_IOERROR: 954 cfg_parser_error(pctx, 0, "%s", 955 isc_result_totext(result)); 956 break; 957 958 default: 959 cfg_parser_error(pctx, CFG_LOG_NEAR, "%s", 960 isc_result_totext(result)); 961 break; 962 } 963 return (result); 964 } 965 966 static void 967 cfg_ungettoken(cfg_parser_t *pctx) { 968 REQUIRE(pctx != NULL); 969 970 if (pctx->seen_eof) 971 return; 972 isc_lex_ungettoken(pctx->lexer, &pctx->token); 973 pctx->ungotten = 1; 974 } 975 976 static isc_result_t 977 cfg_peektoken(cfg_parser_t *pctx, int options) { 978 isc_result_t result; 979 980 REQUIRE(pctx != NULL); 981 982 CHECK(cfg_gettoken(pctx, options)); 983 cfg_ungettoken(pctx); 984 cleanup: 985 return (result); 986 } 987 988 /* 989 * Get a string token, accepting both the quoted and the unquoted form. 990 * Log an error if the next token is not a string. 991 */ 992 static isc_result_t 993 cfg_getstringtoken(cfg_parser_t *pctx) { 994 isc_result_t result; 995 996 result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING); 997 if (result != ISC_R_SUCCESS) 998 return (result); 999 1000 if (pctx->token.type != isc_tokentype_string && 1001 pctx->token.type != isc_tokentype_qstring) { 1002 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string"); 1003 return (ISC_R_UNEXPECTEDTOKEN); 1004 } 1005 return (ISC_R_SUCCESS); 1006 } 1007 1008 static void 1009 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) { 1010 va_list args; 1011 1012 REQUIRE(pctx != NULL); 1013 REQUIRE(fmt != NULL); 1014 1015 va_start(args, fmt); 1016 parser_complain(pctx, 0, flags, fmt, args); 1017 va_end(args); 1018 pctx->errors++; 1019 } 1020 1021 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */ 1022 1023 static int 1024 have_current_file(cfg_parser_t *pctx) { 1025 cfg_listelt_t *elt; 1026 if (pctx->open_files == NULL) 1027 return (0); 1028 1029 elt = ISC_LIST_TAIL(pctx->open_files->value.list); 1030 if (elt == NULL) 1031 return (0); 1032 1033 return (1); 1034 } 1035 1036 static char * 1037 current_file(cfg_parser_t *pctx) { 1038 static char none[] = "none"; 1039 cfg_listelt_t *elt; 1040 cfg_obj_t *fileobj; 1041 1042 if (!have_current_file(pctx)) 1043 return (none); 1044 1045 elt = ISC_LIST_TAIL(pctx->open_files->value.list); 1046 if (elt == NULL) /* shouldn't be possible, but... */ 1047 return (none); 1048 1049 fileobj = elt->obj; 1050 INSIST(fileobj->type == &cfg_type_qstring); 1051 return (fileobj->value.string.base); 1052 } 1053 1054 static void 1055 parser_complain(cfg_parser_t *pctx, int is_warning, 1056 unsigned int flags, const char *format, 1057 va_list args) 1058 { 1059 char tokenbuf[MAX_LOG_TOKEN + 10]; 1060 static char where[PATH_MAX + 100]; 1061 static char message[2048]; 1062 int level = ISC_LOG_ERROR; 1063 const char *prep = ""; 1064 size_t len; 1065 1066 if (is_warning) 1067 level = ISC_LOG_WARNING; 1068 1069 where[0] = '\0'; 1070 if (have_current_file(pctx)) 1071 snprintf(where, sizeof(where), "%s:%u: ", 1072 current_file(pctx), pctx->line); 1073 1074 len = vsnprintf(message, sizeof(message), format, args); 1075 #define ELIPSIS " ... " 1076 if (len >= sizeof(message)) { 1077 message[sizeof(message) - sizeof(ELIPSIS)] = 0; 1078 strlcat(message, ELIPSIS, sizeof(message)); 1079 } 1080 1081 if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) { 1082 isc_region_t r; 1083 1084 if (pctx->ungotten) 1085 (void)cfg_gettoken(pctx, 0); 1086 1087 if (pctx->token.type == isc_tokentype_eof) { 1088 snprintf(tokenbuf, sizeof(tokenbuf), "end of file"); 1089 } else if (pctx->token.type == isc_tokentype_unknown) { 1090 flags = 0; 1091 tokenbuf[0] = '\0'; 1092 } else { 1093 isc_lex_getlasttokentext(pctx->lexer, 1094 &pctx->token, &r); 1095 if (r.length > MAX_LOG_TOKEN) 1096 snprintf(tokenbuf, sizeof(tokenbuf), 1097 "'%.*s...'", MAX_LOG_TOKEN, r.base); 1098 else 1099 snprintf(tokenbuf, sizeof(tokenbuf), 1100 "'%.*s'", (int)r.length, r.base); 1101 } 1102 1103 /* Choose a preposition. */ 1104 if (flags & CFG_LOG_NEAR) 1105 prep = " near "; 1106 else if (flags & CFG_LOG_BEFORE) 1107 prep = " before "; 1108 else 1109 prep = " "; 1110 } else { 1111 tokenbuf[0] = '\0'; 1112 } 1113 isc_log_write(pctx->lctx, CAT, MOD, level, 1114 "%s%s%s%s", where, message, prep, tokenbuf); 1115 } 1116 1117 static isc_result_t 1118 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1119 cfg_obj_t *obj; 1120 1121 REQUIRE(pctx != NULL); 1122 REQUIRE(type != NULL); 1123 REQUIRE(ret != NULL && *ret == NULL); 1124 1125 obj = malloc(sizeof(cfg_obj_t)); 1126 if (obj == NULL) 1127 return (ISC_R_NOMEMORY); 1128 obj->type = type; 1129 obj->file = current_file(pctx); 1130 obj->line = pctx->line; 1131 *ret = obj; 1132 return (ISC_R_SUCCESS); 1133 } 1134 1135 static void 1136 map_symtabitem_destroy(char *key, unsigned int type, 1137 isc_symvalue_t symval, void *userarg) 1138 { 1139 cfg_obj_t *obj = symval.as_pointer; 1140 cfg_parser_t *pctx = (cfg_parser_t *)userarg; 1141 1142 UNUSED(key); 1143 UNUSED(type); 1144 1145 cfg_obj_destroy(pctx, &obj); 1146 } 1147 1148 static isc_result_t 1149 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1150 isc_result_t result; 1151 isc_symtab_t *symtab = NULL; 1152 cfg_obj_t *obj = NULL; 1153 1154 CHECK(cfg_create_obj(pctx, type, &obj)); 1155 CHECK(isc_symtab_create(5, /* XXX */ 1156 map_symtabitem_destroy, 1157 pctx, 0, &symtab)); 1158 obj->value.map.symtab = symtab; 1159 obj->value.map.id = NULL; 1160 1161 *ret = obj; 1162 return (ISC_R_SUCCESS); 1163 1164 cleanup: 1165 if (obj != NULL) 1166 free(obj); 1167 return (result); 1168 } 1169 1170 static void 1171 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) { 1172 CLEANUP_OBJ(obj->value.map.id); 1173 isc_symtab_destroy(&obj->value.map.symtab); 1174 } 1175 1176 /* 1177 * Destroy 'obj', a configuration object created in 'pctx'. 1178 */ 1179 void 1180 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) { 1181 cfg_obj_t *obj; 1182 1183 REQUIRE(objp != NULL && *objp != NULL); 1184 REQUIRE(pctx != NULL); 1185 1186 obj = *objp; 1187 1188 obj->type->rep->free(pctx, obj); 1189 free(obj); 1190 *objp = NULL; 1191 } 1192