1 /* $NetBSD: parser.c,v 1.4 2019/02/24 20:01:32 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * See the COPYRIGHT file distributed with this work for additional 11 * information regarding copyright ownership. 12 */ 13 14 /*! \file */ 15 16 #include <config.h> 17 18 #include <inttypes.h> 19 #include <stdbool.h> 20 #include <stdlib.h> 21 22 #include <isc/buffer.h> 23 #include <isc/dir.h> 24 #include <isc/formatcheck.h> 25 #include <isc/lex.h> 26 #include <isc/log.h> 27 #include <isc/mem.h> 28 #include <isc/net.h> 29 #include <isc/netaddr.h> 30 #include <isc/netscope.h> 31 #include <isc/print.h> 32 #include <isc/string.h> 33 #include <isc/sockaddr.h> 34 #include <isc/symtab.h> 35 #include <isc/util.h> 36 37 #include <isccfg/cfg.h> 38 #include <isccfg/grammar.h> 39 #include <isccfg/log.h> 40 41 /* Shorthand */ 42 #define CAT CFG_LOGCATEGORY_CONFIG 43 #define MOD CFG_LOGMODULE_PARSER 44 45 #define MAP_SYM 1 /* Unique type for isc_symtab */ 46 47 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base) 48 49 /* Check a return value. */ 50 #define CHECK(op) \ 51 do { result = (op); \ 52 if (result != ISC_R_SUCCESS) goto cleanup; \ 53 } while (/*CONSTCOND*/0) 54 55 /* Clean up a configuration object if non-NULL. */ 56 #define CLEANUP_OBJ(obj) \ 57 do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (/*CONSTCOND*/0) 58 59 60 /* 61 * Forward declarations of static functions. 62 */ 63 64 static void 65 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj); 66 67 static isc_result_t 68 parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); 69 70 static void 71 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj); 72 73 static void 74 free_list(cfg_parser_t *pctx, cfg_obj_t *obj); 75 76 static isc_result_t 77 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp); 78 79 static isc_result_t 80 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type, 81 cfg_obj_t **ret); 82 83 static void 84 free_string(cfg_parser_t *pctx, cfg_obj_t *obj); 85 86 static isc_result_t 87 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp); 88 89 static void 90 free_map(cfg_parser_t *pctx, cfg_obj_t *obj); 91 92 static isc_result_t 93 parse_symtab_elt(cfg_parser_t *pctx, const char *name, 94 cfg_type_t *elttype, isc_symtab_t *symtab, 95 bool callback); 96 97 static void 98 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj); 99 100 static isc_result_t 101 cfg_getstringtoken(cfg_parser_t *pctx); 102 103 static void 104 parser_complain(cfg_parser_t *pctx, bool is_warning, 105 unsigned int flags, const char *format, va_list args); 106 107 #ifdef HAVE_GEOIP 108 static isc_result_t 109 parse_geoip(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); 110 111 static void 112 print_geoip(cfg_printer_t *pctx, const cfg_obj_t *obj); 113 114 static void 115 doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type); 116 #endif /* HAVE_GEOIP */ 117 118 /* 119 * Data representations. These correspond to members of the 120 * "value" union in struct cfg_obj (except "void", which does 121 * not need a union member). 122 */ 123 124 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop }; 125 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop }; 126 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_string = { "string", free_string }; 127 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_boolean = { "boolean", free_noop }; 128 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_map = { "map", free_map }; 129 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_list = { "list", free_list }; 130 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple }; 131 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop }; 132 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_netprefix = 133 { "netprefix", free_noop }; 134 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_void = { "void", free_noop }; 135 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_fixedpoint = 136 { "fixedpoint", free_noop }; 137 LIBISCCFG_EXTERNAL_DATA cfg_rep_t cfg_rep_percentage = 138 { "percentage", free_noop }; 139 140 /* 141 * Configuration type definitions. 142 */ 143 144 /*% 145 * An implicit list. These are formed by clauses that occur multiple times. 146 */ 147 static cfg_type_t cfg_type_implicitlist = { 148 "implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL }; 149 150 /* Functions. */ 151 152 void 153 cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) { 154 REQUIRE(pctx != NULL); 155 REQUIRE(obj != NULL); 156 157 obj->type->print(pctx, obj); 158 } 159 160 void 161 cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) { 162 REQUIRE(pctx != NULL); 163 REQUIRE(text != NULL); 164 165 pctx->f(pctx->closure, text, len); 166 } 167 168 static void 169 print_open(cfg_printer_t *pctx) { 170 if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) 171 cfg_print_cstr(pctx, "{ "); 172 else { 173 cfg_print_cstr(pctx, "{\n"); 174 pctx->indent++; 175 } 176 } 177 178 void 179 cfg_print_indent(cfg_printer_t *pctx) { 180 int indent = pctx->indent; 181 if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) { 182 cfg_print_cstr(pctx, " "); 183 return; 184 } 185 while (indent > 0) { 186 cfg_print_cstr(pctx, "\t"); 187 indent--; 188 } 189 } 190 191 static void 192 print_close(cfg_printer_t *pctx) { 193 if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) { 194 pctx->indent--; 195 cfg_print_indent(pctx); 196 } 197 cfg_print_cstr(pctx, "}"); 198 } 199 200 isc_result_t 201 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 202 isc_result_t result; 203 204 REQUIRE(pctx != NULL); 205 REQUIRE(type != NULL); 206 REQUIRE(ret != NULL && *ret == NULL); 207 208 result = type->parse(pctx, type, ret); 209 if (result != ISC_R_SUCCESS) 210 return (result); 211 ENSURE(*ret != NULL); 212 return (ISC_R_SUCCESS); 213 } 214 215 void 216 cfg_print(const cfg_obj_t *obj, 217 void (*f)(void *closure, const char *text, int textlen), 218 void *closure) 219 { 220 REQUIRE(obj != NULL); 221 REQUIRE(f != NULL); 222 223 cfg_printx(obj, 0, f, closure); 224 } 225 226 void 227 cfg_printx(const cfg_obj_t *obj, unsigned int flags, 228 void (*f)(void *closure, const char *text, int textlen), 229 void *closure) 230 { 231 cfg_printer_t pctx; 232 233 REQUIRE(obj != NULL); 234 REQUIRE(f != NULL); 235 236 pctx.f = f; 237 pctx.closure = closure; 238 pctx.indent = 0; 239 pctx.flags = flags; 240 obj->type->print(&pctx, obj); 241 } 242 243 /* Tuples. */ 244 245 isc_result_t 246 cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 247 isc_result_t result; 248 const cfg_tuplefielddef_t *fields = type->of; 249 const cfg_tuplefielddef_t *f; 250 cfg_obj_t *obj = NULL; 251 unsigned int nfields = 0; 252 int i; 253 254 REQUIRE(pctx != NULL); 255 REQUIRE(type != NULL); 256 REQUIRE(ret != NULL && *ret == NULL); 257 258 for (f = fields; f->name != NULL; f++) 259 nfields++; 260 261 CHECK(cfg_create_obj(pctx, type, &obj)); 262 obj->value.tuple = isc_mem_get(pctx->mctx, 263 nfields * sizeof(cfg_obj_t *)); 264 if (obj->value.tuple == NULL) { 265 result = ISC_R_NOMEMORY; 266 goto cleanup; 267 } 268 for (f = fields, i = 0; f->name != NULL; f++, i++) 269 obj->value.tuple[i] = NULL; 270 *ret = obj; 271 return (ISC_R_SUCCESS); 272 273 cleanup: 274 if (obj != NULL) 275 isc_mem_put(pctx->mctx, obj, sizeof(*obj)); 276 return (result); 277 } 278 279 isc_result_t 280 cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) 281 { 282 isc_result_t result; 283 const cfg_tuplefielddef_t *fields = type->of; 284 const cfg_tuplefielddef_t *f; 285 cfg_obj_t *obj = NULL; 286 unsigned int i; 287 288 REQUIRE(pctx != NULL); 289 REQUIRE(type != NULL); 290 REQUIRE(ret != NULL && *ret == NULL); 291 292 CHECK(cfg_create_tuple(pctx, type, &obj)); 293 for (f = fields, i = 0; f->name != NULL; f++, i++) 294 CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i])); 295 296 *ret = obj; 297 return (ISC_R_SUCCESS); 298 299 cleanup: 300 CLEANUP_OBJ(obj); 301 return (result); 302 } 303 304 void 305 cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) { 306 unsigned int i; 307 const cfg_tuplefielddef_t *fields; 308 const cfg_tuplefielddef_t *f; 309 bool need_space = false; 310 311 REQUIRE(pctx != NULL); 312 REQUIRE(obj != NULL); 313 314 fields = obj->type->of; 315 316 for (f = fields, i = 0; f->name != NULL; f++, i++) { 317 const cfg_obj_t *fieldobj = obj->value.tuple[i]; 318 if (need_space && fieldobj->type->rep != &cfg_rep_void) 319 cfg_print_cstr(pctx, " "); 320 cfg_print_obj(pctx, fieldobj); 321 need_space = (need_space || 322 fieldobj->type->print != cfg_print_void); 323 } 324 } 325 326 void 327 cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) { 328 const cfg_tuplefielddef_t *fields; 329 const cfg_tuplefielddef_t *f; 330 bool need_space = false; 331 332 REQUIRE(pctx != NULL); 333 REQUIRE(type != NULL); 334 335 fields = type->of; 336 337 for (f = fields; f->name != NULL; f++) { 338 if (need_space) 339 cfg_print_cstr(pctx, " "); 340 cfg_doc_obj(pctx, f->type); 341 need_space = (f->type->print != cfg_print_void); 342 } 343 } 344 345 static void 346 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) { 347 unsigned int i; 348 const cfg_tuplefielddef_t *fields = obj->type->of; 349 const cfg_tuplefielddef_t *f; 350 unsigned int nfields = 0; 351 352 if (obj->value.tuple == NULL) 353 return; 354 355 for (f = fields, i = 0; f->name != NULL; f++, i++) { 356 CLEANUP_OBJ(obj->value.tuple[i]); 357 nfields++; 358 } 359 isc_mem_put(pctx->mctx, obj->value.tuple, 360 nfields * sizeof(cfg_obj_t *)); 361 } 362 363 bool 364 cfg_obj_istuple(const cfg_obj_t *obj) { 365 REQUIRE(obj != NULL); 366 return (obj->type->rep == &cfg_rep_tuple); 367 } 368 369 const cfg_obj_t * 370 cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) { 371 unsigned int i; 372 const cfg_tuplefielddef_t *fields; 373 const cfg_tuplefielddef_t *f; 374 375 REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple); 376 REQUIRE(name != NULL); 377 378 fields = tupleobj->type->of; 379 for (f = fields, i = 0; f->name != NULL; f++, i++) { 380 if (strcmp(f->name, name) == 0) 381 return (tupleobj->value.tuple[i]); 382 } 383 INSIST(0); 384 ISC_UNREACHABLE(); 385 } 386 387 isc_result_t 388 cfg_parse_special(cfg_parser_t *pctx, int special) { 389 isc_result_t result; 390 391 REQUIRE(pctx != NULL); 392 393 CHECK(cfg_gettoken(pctx, 0)); 394 if (pctx->token.type == isc_tokentype_special && 395 pctx->token.value.as_char == special) 396 return (ISC_R_SUCCESS); 397 398 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special); 399 return (ISC_R_UNEXPECTEDTOKEN); 400 cleanup: 401 return (result); 402 } 403 404 /* 405 * Parse a required semicolon. If it is not there, log 406 * an error and increment the error count but continue 407 * parsing. Since the next token is pushed back, 408 * care must be taken to make sure it is eventually 409 * consumed or an infinite loop may result. 410 */ 411 static isc_result_t 412 parse_semicolon(cfg_parser_t *pctx) { 413 isc_result_t result; 414 415 CHECK(cfg_gettoken(pctx, 0)); 416 if (pctx->token.type == isc_tokentype_special && 417 pctx->token.value.as_char == ';') 418 return (ISC_R_SUCCESS); 419 420 cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'"); 421 cfg_ungettoken(pctx); 422 cleanup: 423 return (result); 424 } 425 426 /* 427 * Parse EOF, logging and returning an error if not there. 428 */ 429 static isc_result_t 430 parse_eof(cfg_parser_t *pctx) { 431 isc_result_t result; 432 433 CHECK(cfg_gettoken(pctx, 0)); 434 435 if (pctx->token.type == isc_tokentype_eof) 436 return (ISC_R_SUCCESS); 437 438 cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error"); 439 return (ISC_R_UNEXPECTEDTOKEN); 440 cleanup: 441 return (result); 442 } 443 444 /* A list of files, used internally for pctx->files. */ 445 446 static cfg_type_t cfg_type_filelist = { 447 "filelist", NULL, print_list, NULL, &cfg_rep_list, 448 &cfg_type_qstring 449 }; 450 451 isc_result_t 452 cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) { 453 isc_result_t result; 454 cfg_parser_t *pctx; 455 isc_lexspecials_t specials; 456 457 REQUIRE(mctx != NULL); 458 REQUIRE(ret != NULL && *ret == NULL); 459 460 pctx = isc_mem_get(mctx, sizeof(*pctx)); 461 if (pctx == NULL) 462 return (ISC_R_NOMEMORY); 463 464 pctx->mctx = NULL; 465 isc_mem_attach(mctx, &pctx->mctx); 466 467 isc_refcount_init(&pctx->references, 1); 468 469 pctx->lctx = lctx; 470 pctx->lexer = NULL; 471 pctx->seen_eof = false; 472 pctx->ungotten = false; 473 pctx->errors = 0; 474 pctx->warnings = 0; 475 pctx->open_files = NULL; 476 pctx->closed_files = NULL; 477 pctx->line = 0; 478 pctx->callback = NULL; 479 pctx->callbackarg = NULL; 480 pctx->token.type = isc_tokentype_unknown; 481 pctx->flags = 0; 482 pctx->buf_name = NULL; 483 484 memset(specials, 0, sizeof(specials)); 485 specials['{'] = 1; 486 specials['}'] = 1; 487 specials[';'] = 1; 488 specials['/'] = 1; 489 specials['"'] = 1; 490 specials['!'] = 1; 491 492 CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer)); 493 494 isc_lex_setspecials(pctx->lexer, specials); 495 isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C | 496 ISC_LEXCOMMENT_CPLUSPLUS | 497 ISC_LEXCOMMENT_SHELL)); 498 499 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files)); 500 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files)); 501 502 *ret = pctx; 503 return (ISC_R_SUCCESS); 504 505 cleanup: 506 if (pctx->lexer != NULL) 507 isc_lex_destroy(&pctx->lexer); 508 CLEANUP_OBJ(pctx->open_files); 509 CLEANUP_OBJ(pctx->closed_files); 510 isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx)); 511 return (result); 512 } 513 514 static isc_result_t 515 parser_openfile(cfg_parser_t *pctx, const char *filename) { 516 isc_result_t result; 517 cfg_listelt_t *elt = NULL; 518 cfg_obj_t *stringobj = NULL; 519 520 result = isc_lex_openfile(pctx->lexer, filename); 521 if (result != ISC_R_SUCCESS) { 522 cfg_parser_error(pctx, 0, "open: %s: %s", 523 filename, isc_result_totext(result)); 524 goto cleanup; 525 } 526 527 CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj)); 528 CHECK(create_listelt(pctx, &elt)); 529 elt->obj = stringobj; 530 ISC_LIST_APPEND(pctx->open_files->value.list, elt, link); 531 532 return (ISC_R_SUCCESS); 533 cleanup: 534 CLEANUP_OBJ(stringobj); 535 return (result); 536 } 537 538 void 539 cfg_parser_setcallback(cfg_parser_t *pctx, 540 cfg_parsecallback_t callback, 541 void *arg) 542 { 543 REQUIRE(pctx != NULL); 544 545 pctx->callback = callback; 546 pctx->callbackarg = arg; 547 } 548 549 void 550 cfg_parser_reset(cfg_parser_t *pctx) { 551 REQUIRE(pctx != NULL); 552 553 if (pctx->lexer != NULL) 554 isc_lex_close(pctx->lexer); 555 556 pctx->seen_eof = false; 557 pctx->ungotten = false; 558 pctx->errors = 0; 559 pctx->warnings = 0; 560 pctx->line = 0; 561 } 562 563 /* 564 * Parse a configuration using a pctx where a lexer has already 565 * been set up with a source. 566 */ 567 static isc_result_t 568 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 569 isc_result_t result; 570 cfg_obj_t *obj = NULL; 571 572 result = cfg_parse_obj(pctx, type, &obj); 573 574 if (pctx->errors != 0) { 575 /* Errors have been logged. */ 576 if (result == ISC_R_SUCCESS) 577 result = ISC_R_FAILURE; 578 goto cleanup; 579 } 580 581 if (result != ISC_R_SUCCESS) { 582 /* Parsing failed but no errors have been logged. */ 583 cfg_parser_error(pctx, 0, "parsing failed: %s", 584 isc_result_totext(result)); 585 goto cleanup; 586 } 587 588 CHECK(parse_eof(pctx)); 589 590 *ret = obj; 591 return (ISC_R_SUCCESS); 592 593 cleanup: 594 CLEANUP_OBJ(obj); 595 return (result); 596 } 597 598 isc_result_t 599 cfg_parse_file(cfg_parser_t *pctx, const char *filename, 600 const cfg_type_t *type, cfg_obj_t **ret) 601 { 602 isc_result_t result; 603 cfg_listelt_t *elt; 604 605 REQUIRE(pctx != NULL); 606 REQUIRE(filename != NULL); 607 REQUIRE(type != NULL); 608 REQUIRE(ret != NULL && *ret == NULL); 609 610 CHECK(parser_openfile(pctx, filename)); 611 612 result = parse2(pctx, type, ret); 613 614 /* Clean up the opened file */ 615 elt = ISC_LIST_TAIL(pctx->open_files->value.list); 616 INSIST(elt != NULL); 617 ISC_LIST_UNLINK(pctx->open_files->value.list, elt, link); 618 ISC_LIST_APPEND(pctx->closed_files->value.list, elt, link); 619 620 cleanup: 621 return (result); 622 } 623 624 625 isc_result_t 626 cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer, 627 const char *file, unsigned int line, 628 const cfg_type_t *type, unsigned int flags, 629 cfg_obj_t **ret) 630 { 631 isc_result_t result; 632 633 REQUIRE(pctx != NULL); 634 REQUIRE(type != NULL); 635 REQUIRE(buffer != NULL); 636 REQUIRE(ret != NULL && *ret == NULL); 637 REQUIRE((flags & ~(CFG_PCTX_NODEPRECATED)) == 0); 638 639 CHECK(isc_lex_openbuffer(pctx->lexer, buffer)); 640 641 pctx->buf_name = file; 642 pctx->flags = flags; 643 644 if (line != 0U) 645 CHECK(isc_lex_setsourceline(pctx->lexer, line)); 646 647 CHECK(parse2(pctx, type, ret)); 648 pctx->buf_name = NULL; 649 650 cleanup: 651 return (result); 652 } 653 654 void 655 cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) { 656 REQUIRE(src != NULL); 657 REQUIRE(dest != NULL && *dest == NULL); 658 659 isc_refcount_increment(&src->references); 660 *dest = src; 661 } 662 663 void 664 cfg_parser_destroy(cfg_parser_t **pctxp) { 665 cfg_parser_t *pctx; 666 667 REQUIRE(pctxp != NULL && *pctxp != NULL); 668 pctx = *pctxp; 669 *pctxp = NULL; 670 671 if (isc_refcount_decrement(&pctx->references) == 1) { 672 isc_lex_destroy(&pctx->lexer); 673 /* 674 * Cleaning up open_files does not 675 * close the files; that was already done 676 * by closing the lexer. 677 */ 678 CLEANUP_OBJ(pctx->open_files); 679 CLEANUP_OBJ(pctx->closed_files); 680 isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx)); 681 } 682 } 683 684 /* 685 * void 686 */ 687 isc_result_t 688 cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 689 REQUIRE(pctx != NULL); 690 REQUIRE(ret != NULL && *ret == NULL); 691 692 UNUSED(type); 693 694 return (cfg_create_obj(pctx, &cfg_type_void, ret)); 695 } 696 697 void 698 cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) { 699 700 REQUIRE(pctx != NULL); 701 REQUIRE(obj != NULL); 702 703 UNUSED(pctx); 704 UNUSED(obj); 705 } 706 707 void 708 cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) { 709 710 REQUIRE(pctx != NULL); 711 REQUIRE(type != NULL); 712 713 UNUSED(pctx); 714 UNUSED(type); 715 } 716 717 bool 718 cfg_obj_isvoid(const cfg_obj_t *obj) { 719 REQUIRE(obj != NULL); 720 return (obj->type->rep == &cfg_rep_void); 721 } 722 723 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_void = { 724 "void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void, 725 NULL }; 726 727 /* 728 * percentage 729 */ 730 isc_result_t 731 cfg_parse_percentage(cfg_parser_t *pctx, const cfg_type_t *type, 732 cfg_obj_t **ret) 733 { 734 char *endp; 735 isc_result_t result; 736 cfg_obj_t *obj = NULL; 737 uint64_t percent; 738 739 REQUIRE(pctx != NULL); 740 REQUIRE(ret != NULL && *ret == NULL); 741 742 UNUSED(type); 743 744 CHECK(cfg_gettoken(pctx, 0)); 745 if (pctx->token.type != isc_tokentype_string) { 746 cfg_parser_error(pctx, CFG_LOG_NEAR, 747 "expected percentage"); 748 return (ISC_R_UNEXPECTEDTOKEN); 749 } 750 751 percent = strtoull(TOKEN_STRING(pctx), &endp, 10); 752 if (*endp != '%' || *(endp+1) != 0) { 753 cfg_parser_error(pctx, CFG_LOG_NEAR, 754 "expected percentage"); 755 return (ISC_R_UNEXPECTEDTOKEN); 756 } 757 758 CHECK(cfg_create_obj(pctx, &cfg_type_percentage, &obj)); 759 obj->value.uint32 = (uint32_t)percent; 760 *ret = obj; 761 762 cleanup: 763 return (result); 764 } 765 766 void 767 cfg_print_percentage(cfg_printer_t *pctx, const cfg_obj_t *obj) { 768 char buf[64]; 769 int n; 770 771 REQUIRE(pctx != NULL); 772 REQUIRE(obj != NULL); 773 774 n = snprintf(buf, sizeof(buf), "%u%%", obj->value.uint32); 775 INSIST(n > 0 && (size_t)n < sizeof(buf)); 776 cfg_print_chars(pctx, buf, strlen(buf)); 777 } 778 779 uint32_t 780 cfg_obj_aspercentage(const cfg_obj_t *obj) { 781 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_percentage); 782 return (obj->value.uint32); 783 } 784 785 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_percentage = { 786 "percentage", cfg_parse_percentage, cfg_print_percentage, 787 cfg_doc_terminal, &cfg_rep_percentage, NULL 788 }; 789 790 bool 791 cfg_obj_ispercentage(const cfg_obj_t *obj) { 792 REQUIRE(obj != NULL); 793 return (obj->type->rep == &cfg_rep_percentage); 794 } 795 796 /* 797 * Fixed point 798 */ 799 isc_result_t 800 cfg_parse_fixedpoint(cfg_parser_t *pctx, const cfg_type_t *type, 801 cfg_obj_t **ret) 802 { 803 isc_result_t result; 804 cfg_obj_t *obj = NULL; 805 size_t n1, n2, n3, l; 806 const char *p; 807 808 REQUIRE(pctx != NULL); 809 REQUIRE(ret != NULL && *ret == NULL); 810 811 UNUSED(type); 812 813 CHECK(cfg_gettoken(pctx, 0)); 814 if (pctx->token.type != isc_tokentype_string) { 815 cfg_parser_error(pctx, CFG_LOG_NEAR, 816 "expected fixed point number"); 817 return (ISC_R_UNEXPECTEDTOKEN); 818 } 819 820 821 p = TOKEN_STRING(pctx); 822 l = strlen(p); 823 n1 = strspn(p, "0123456789"); 824 n2 = strspn(p + n1, "."); 825 n3 = strspn(p + n1 + n2, "0123456789"); 826 827 if ((n1 + n2 + n3 != l) || (n1 + n3 == 0) || 828 n1 > 5 || n2 > 1 || n3 > 2) { 829 cfg_parser_error(pctx, CFG_LOG_NEAR, 830 "expected fixed point number"); 831 return (ISC_R_UNEXPECTEDTOKEN); 832 } 833 834 CHECK(cfg_create_obj(pctx, &cfg_type_fixedpoint, &obj)); 835 836 obj->value.uint32 = strtoul(p, NULL, 10) * 100; 837 switch (n3) { 838 case 2: 839 obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10); 840 break; 841 case 1: 842 obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10) * 10; 843 break; 844 } 845 *ret = obj; 846 847 cleanup: 848 return (result); 849 } 850 851 void 852 cfg_print_fixedpoint(cfg_printer_t *pctx, const cfg_obj_t *obj) { 853 char buf[64]; 854 int n; 855 856 REQUIRE(pctx != NULL); 857 REQUIRE(obj != NULL); 858 859 n = snprintf(buf, sizeof(buf), "%u.%02u", 860 obj->value.uint32/100, obj->value.uint32%100); 861 INSIST(n > 0 && (size_t)n < sizeof(buf)); 862 cfg_print_chars(pctx, buf, strlen(buf)); 863 } 864 865 uint32_t 866 cfg_obj_asfixedpoint(const cfg_obj_t *obj) { 867 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_fixedpoint); 868 return (obj->value.uint32); 869 } 870 871 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_fixedpoint = { 872 "fixedpoint", cfg_parse_fixedpoint, cfg_print_fixedpoint, 873 cfg_doc_terminal, &cfg_rep_fixedpoint, NULL 874 }; 875 876 bool 877 cfg_obj_isfixedpoint(const cfg_obj_t *obj) { 878 REQUIRE(obj != NULL); 879 return (obj->type->rep == &cfg_rep_fixedpoint); 880 } 881 882 /* 883 * uint32 884 */ 885 isc_result_t 886 cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 887 isc_result_t result; 888 cfg_obj_t *obj = NULL; 889 890 REQUIRE(pctx != NULL); 891 REQUIRE(ret != NULL && *ret == NULL); 892 893 UNUSED(type); 894 895 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER)); 896 if (pctx->token.type != isc_tokentype_number) { 897 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number"); 898 return (ISC_R_UNEXPECTEDTOKEN); 899 } 900 901 CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj)); 902 903 obj->value.uint32 = pctx->token.value.as_ulong; 904 *ret = obj; 905 cleanup: 906 return (result); 907 } 908 909 void 910 cfg_print_cstr(cfg_printer_t *pctx, const char *s) { 911 cfg_print_chars(pctx, s, strlen(s)); 912 } 913 914 void 915 cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) { 916 char buf[32]; 917 918 snprintf(buf, sizeof(buf), "%u", u); 919 cfg_print_cstr(pctx, buf); 920 } 921 922 void 923 cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) { 924 cfg_print_rawuint(pctx, obj->value.uint32); 925 } 926 927 bool 928 cfg_obj_isuint32(const cfg_obj_t *obj) { 929 REQUIRE(obj != NULL); 930 return (obj->type->rep == &cfg_rep_uint32); 931 } 932 933 uint32_t 934 cfg_obj_asuint32(const cfg_obj_t *obj) { 935 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32); 936 return (obj->value.uint32); 937 } 938 939 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_uint32 = { 940 "integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal, 941 &cfg_rep_uint32, NULL 942 }; 943 944 945 /* 946 * uint64 947 */ 948 bool 949 cfg_obj_isuint64(const cfg_obj_t *obj) { 950 REQUIRE(obj != NULL); 951 return (obj->type->rep == &cfg_rep_uint64); 952 } 953 954 uint64_t 955 cfg_obj_asuint64(const cfg_obj_t *obj) { 956 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64); 957 return (obj->value.uint64); 958 } 959 960 void 961 cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) { 962 char buf[32]; 963 964 snprintf(buf, sizeof(buf), "%" PRIu64, 965 obj->value.uint64); 966 cfg_print_cstr(pctx, buf); 967 } 968 969 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_uint64 = { 970 "64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal, 971 &cfg_rep_uint64, NULL 972 }; 973 974 /* 975 * qstring (quoted string), ustring (unquoted string), astring 976 * (any string) 977 */ 978 979 /* Create a string object from a null-terminated C string. */ 980 static isc_result_t 981 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type, 982 cfg_obj_t **ret) 983 { 984 isc_result_t result; 985 cfg_obj_t *obj = NULL; 986 int len; 987 988 CHECK(cfg_create_obj(pctx, type, &obj)); 989 len = strlen(contents); 990 obj->value.string.length = len; 991 obj->value.string.base = isc_mem_get(pctx->mctx, len + 1); 992 if (obj->value.string.base == 0) { 993 isc_mem_put(pctx->mctx, obj, sizeof(*obj)); 994 return (ISC_R_NOMEMORY); 995 } 996 memmove(obj->value.string.base, contents, len); 997 obj->value.string.base[len] = '\0'; 998 999 *ret = obj; 1000 cleanup: 1001 return (result); 1002 } 1003 1004 isc_result_t 1005 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1006 isc_result_t result; 1007 1008 REQUIRE(pctx != NULL); 1009 REQUIRE(ret != NULL && *ret == NULL); 1010 1011 UNUSED(type); 1012 1013 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); 1014 if (pctx->token.type != isc_tokentype_qstring) { 1015 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string"); 1016 return (ISC_R_UNEXPECTEDTOKEN); 1017 } 1018 return (create_string(pctx, TOKEN_STRING(pctx), 1019 &cfg_type_qstring, ret)); 1020 cleanup: 1021 return (result); 1022 } 1023 1024 static isc_result_t 1025 parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1026 isc_result_t result; 1027 1028 UNUSED(type); 1029 1030 CHECK(cfg_gettoken(pctx, 0)); 1031 if (pctx->token.type != isc_tokentype_string) { 1032 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string"); 1033 return (ISC_R_UNEXPECTEDTOKEN); 1034 } 1035 return (create_string(pctx, 1036 TOKEN_STRING(pctx), 1037 &cfg_type_ustring, 1038 ret)); 1039 cleanup: 1040 return (result); 1041 } 1042 1043 isc_result_t 1044 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, 1045 cfg_obj_t **ret) 1046 { 1047 isc_result_t result; 1048 1049 REQUIRE(pctx != NULL); 1050 REQUIRE(ret != NULL && *ret == NULL); 1051 1052 UNUSED(type); 1053 1054 CHECK(cfg_getstringtoken(pctx)); 1055 return (create_string(pctx, 1056 TOKEN_STRING(pctx), 1057 &cfg_type_qstring, 1058 ret)); 1059 cleanup: 1060 return (result); 1061 } 1062 1063 isc_result_t 1064 cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type, 1065 cfg_obj_t **ret) 1066 { 1067 isc_result_t result; 1068 1069 REQUIRE(pctx != NULL); 1070 REQUIRE(ret != NULL && *ret == NULL); 1071 1072 UNUSED(type); 1073 1074 CHECK(cfg_getstringtoken(pctx)); 1075 return (create_string(pctx, 1076 TOKEN_STRING(pctx), 1077 &cfg_type_sstring, 1078 ret)); 1079 cleanup: 1080 return (result); 1081 } 1082 1083 static isc_result_t 1084 parse_btext(cfg_parser_t *pctx, const cfg_type_t *type, 1085 cfg_obj_t **ret) 1086 { 1087 isc_result_t result; 1088 1089 UNUSED(type); 1090 1091 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_BTEXT)); 1092 if (pctx->token.type != isc_tokentype_btext) { 1093 cfg_parser_error(pctx, CFG_LOG_NEAR, 1094 "expected bracketed text"); 1095 return (ISC_R_UNEXPECTEDTOKEN); 1096 } 1097 return (create_string(pctx, 1098 TOKEN_STRING(pctx), 1099 &cfg_type_bracketed_text, 1100 ret)); 1101 cleanup: 1102 return (result); 1103 } 1104 1105 static void 1106 print_btext(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1107 /* 1108 * We need to print "{" instead of running print_open() 1109 * in order to preserve the exact original formatting 1110 * of the bracketed text. But we increment the indent value 1111 * so that print_close() will leave us back in our original 1112 * state. 1113 */ 1114 pctx->indent++; 1115 cfg_print_cstr(pctx, "{"); 1116 cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length); 1117 print_close(pctx); 1118 } 1119 1120 static void 1121 doc_btext(cfg_printer_t *pctx, const cfg_type_t *type) { 1122 UNUSED(type); 1123 1124 cfg_print_cstr(pctx, "{ <unspecified-text> }"); 1125 } 1126 1127 bool 1128 cfg_is_enum(const char *s, const char *const *enums) { 1129 const char * const *p; 1130 1131 REQUIRE(s != NULL); 1132 REQUIRE(enums != NULL); 1133 1134 for (p = enums; *p != NULL; p++) { 1135 if (strcasecmp(*p, s) == 0) 1136 return (true); 1137 } 1138 return (false); 1139 } 1140 1141 static isc_result_t 1142 check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) { 1143 const char *s = obj->value.string.base; 1144 1145 if (cfg_is_enum(s, enums)) 1146 return (ISC_R_SUCCESS); 1147 cfg_parser_error(pctx, 0, "'%s' unexpected", s); 1148 return (ISC_R_UNEXPECTEDTOKEN); 1149 } 1150 1151 isc_result_t 1152 cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1153 isc_result_t result; 1154 cfg_obj_t *obj = NULL; 1155 1156 REQUIRE(pctx != NULL); 1157 REQUIRE(type != NULL); 1158 REQUIRE(ret != NULL && *ret == NULL); 1159 1160 CHECK(parse_ustring(pctx, NULL, &obj)); 1161 CHECK(check_enum(pctx, obj, type->of)); 1162 *ret = obj; 1163 return (ISC_R_SUCCESS); 1164 cleanup: 1165 CLEANUP_OBJ(obj); 1166 return (result); 1167 } 1168 1169 void 1170 cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) { 1171 const char * const *p; 1172 1173 REQUIRE(pctx != NULL); 1174 REQUIRE(type != NULL); 1175 1176 cfg_print_cstr(pctx, "( "); 1177 for (p = type->of; *p != NULL; p++) { 1178 cfg_print_cstr(pctx, *p); 1179 if (p[1] != NULL) 1180 cfg_print_cstr(pctx, " | "); 1181 } 1182 cfg_print_cstr(pctx, " )"); 1183 } 1184 1185 isc_result_t 1186 cfg_parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype, 1187 const cfg_type_t *othertype, cfg_obj_t **ret) 1188 { 1189 isc_result_t result; 1190 CHECK(cfg_peektoken(pctx, 0)); 1191 if (pctx->token.type == isc_tokentype_string && 1192 cfg_is_enum(TOKEN_STRING(pctx), enumtype->of)) 1193 { 1194 CHECK(cfg_parse_enum(pctx, enumtype, ret)); 1195 } else { 1196 CHECK(cfg_parse_obj(pctx, othertype, ret)); 1197 } 1198 cleanup: 1199 return (result); 1200 } 1201 1202 void 1203 cfg_doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *enumtype, 1204 const cfg_type_t *othertype) 1205 { 1206 const char * const *p; 1207 bool first = true; 1208 1209 /* 1210 * If othertype is cfg_type_void, it means that enumtype is 1211 * optional. 1212 */ 1213 1214 if (othertype == &cfg_type_void) { 1215 cfg_print_cstr(pctx, "[ "); 1216 } 1217 cfg_print_cstr(pctx, "( "); 1218 for (p = enumtype->of; *p != NULL; p++) { 1219 if (!first) { 1220 cfg_print_cstr(pctx, " | "); 1221 } 1222 first = false; 1223 cfg_print_cstr(pctx, *p); 1224 } 1225 if (othertype != &cfg_type_void) { 1226 if (!first) { 1227 cfg_print_cstr(pctx, " | "); 1228 } 1229 cfg_doc_terminal(pctx, othertype); 1230 } 1231 cfg_print_cstr(pctx, " )"); 1232 if (othertype == &cfg_type_void) { 1233 cfg_print_cstr(pctx, " ]"); 1234 } 1235 } 1236 1237 void 1238 cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1239 REQUIRE(pctx != NULL); 1240 REQUIRE(obj != NULL); 1241 1242 cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length); 1243 } 1244 1245 static void 1246 print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1247 cfg_print_cstr(pctx, "\""); 1248 cfg_print_ustring(pctx, obj); 1249 cfg_print_cstr(pctx, "\""); 1250 } 1251 1252 static void 1253 print_sstring(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1254 cfg_print_cstr(pctx, "\""); 1255 if ((pctx->flags & CFG_PRINTER_XKEY) != 0) { 1256 unsigned int len = obj->value.string.length; 1257 while (len-- > 0) 1258 cfg_print_cstr(pctx, "?"); 1259 } else 1260 cfg_print_ustring(pctx, obj); 1261 cfg_print_cstr(pctx, "\""); 1262 } 1263 1264 static void 1265 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) { 1266 isc_mem_put(pctx->mctx, obj->value.string.base, 1267 obj->value.string.length + 1); 1268 } 1269 1270 bool 1271 cfg_obj_isstring(const cfg_obj_t *obj) { 1272 REQUIRE(obj != NULL); 1273 return (obj->type->rep == &cfg_rep_string); 1274 } 1275 1276 const char * 1277 cfg_obj_asstring(const cfg_obj_t *obj) { 1278 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string); 1279 return (obj->value.string.base); 1280 } 1281 1282 /* Quoted string only */ 1283 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_qstring = { 1284 "quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal, 1285 &cfg_rep_string, NULL 1286 }; 1287 1288 /* Unquoted string only */ 1289 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_ustring = { 1290 "string", parse_ustring, cfg_print_ustring, cfg_doc_terminal, 1291 &cfg_rep_string, NULL 1292 }; 1293 1294 /* Any string (quoted or unquoted); printed with quotes */ 1295 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_astring = { 1296 "string", cfg_parse_astring, print_qstring, cfg_doc_terminal, 1297 &cfg_rep_string, NULL 1298 }; 1299 1300 /* 1301 * Any string (quoted or unquoted); printed with quotes. 1302 * If CFG_PRINTER_XKEY is set when printing the string will be '?' out. 1303 */ 1304 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_sstring = { 1305 "string", cfg_parse_sstring, print_sstring, cfg_doc_terminal, 1306 &cfg_rep_string, NULL 1307 }; 1308 1309 /* 1310 * Text enclosed in brackets. Used to pass a block of configuration 1311 * text to dynamic library or external application. Checked for 1312 * bracket balance, but not otherwise parsed. 1313 */ 1314 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_bracketed_text = { 1315 "bracketed_text", parse_btext, print_btext, doc_btext, 1316 &cfg_rep_string, NULL 1317 }; 1318 1319 #ifdef HAVE_GEOIP 1320 /* 1321 * "geoip" ACL element: 1322 * geoip [ db <database> ] search-type <string> 1323 */ 1324 static const char *geoiptype_enums[] = { 1325 "area", "areacode", "asnum", "city", "continent", "country", 1326 "country3", "countryname", "domain", "isp", "metro", "metrocode", 1327 "netspeed", "org", "postal", "postalcode", "region", "regionname", 1328 "timezone", "tz", NULL 1329 }; 1330 static cfg_type_t cfg_type_geoiptype = { 1331 "geoiptype", cfg_parse_enum, cfg_print_ustring, 1332 cfg_doc_enum, &cfg_rep_string, &geoiptype_enums 1333 }; 1334 1335 static const char *geoipdb_enums[] = { 1336 "asnum", "city", "country", "domain", "isp", "netspeed", 1337 "org", "region", NULL 1338 }; 1339 static cfg_type_t cfg_type_geoipdb = { 1340 "geoipdb", cfg_parse_enum, cfg_print_ustring, 1341 cfg_doc_enum, &cfg_rep_string, &geoipdb_enums 1342 }; 1343 1344 static cfg_tuplefielddef_t geoip_fields[] = { 1345 { "negated", &cfg_type_void, 0 }, 1346 { "db", &cfg_type_geoipdb, 0 }, 1347 { "subtype", &cfg_type_geoiptype, 0 }, 1348 { "search", &cfg_type_astring, 0 }, 1349 { NULL, NULL, 0 } 1350 }; 1351 1352 static cfg_type_t cfg_type_geoip = { 1353 "geoip", parse_geoip, print_geoip, doc_geoip, 1354 &cfg_rep_tuple, geoip_fields 1355 }; 1356 1357 static isc_result_t 1358 parse_geoip(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1359 isc_result_t result; 1360 cfg_obj_t *obj = NULL; 1361 const cfg_tuplefielddef_t *fields = type->of; 1362 1363 CHECK(cfg_create_tuple(pctx, type, &obj)); 1364 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[0])); 1365 1366 /* Parse the optional "db" field. */ 1367 CHECK(cfg_peektoken(pctx, 0)); 1368 if (pctx->token.type == isc_tokentype_string) { 1369 CHECK(cfg_gettoken(pctx, 0)); 1370 if (strcasecmp(TOKEN_STRING(pctx), "db") == 0 && 1371 obj->value.tuple[1] == NULL) { 1372 CHECK(cfg_parse_obj(pctx, fields[1].type, 1373 &obj->value.tuple[1])); 1374 } else { 1375 CHECK(cfg_parse_void(pctx, NULL, 1376 &obj->value.tuple[1])); 1377 cfg_ungettoken(pctx); 1378 } 1379 } 1380 1381 CHECK(cfg_parse_obj(pctx, fields[2].type, &obj->value.tuple[2])); 1382 CHECK(cfg_parse_obj(pctx, fields[3].type, &obj->value.tuple[3])); 1383 1384 *ret = obj; 1385 return (ISC_R_SUCCESS); 1386 1387 cleanup: 1388 CLEANUP_OBJ(obj); 1389 return (result); 1390 } 1391 1392 static void 1393 print_geoip(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1394 if (obj->value.tuple[1]->type->print != cfg_print_void) { 1395 cfg_print_cstr(pctx, " db "); 1396 cfg_print_obj(pctx, obj->value.tuple[1]); 1397 } 1398 cfg_print_obj(pctx, obj->value.tuple[2]); 1399 cfg_print_obj(pctx, obj->value.tuple[3]); 1400 } 1401 1402 static void 1403 doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type) { 1404 UNUSED(type); 1405 cfg_print_cstr(pctx, "[ db "); 1406 cfg_doc_enum(pctx, &cfg_type_geoipdb); 1407 cfg_print_cstr(pctx, " ]"); 1408 cfg_print_cstr(pctx, " "); 1409 cfg_doc_enum(pctx, &cfg_type_geoiptype); 1410 cfg_print_cstr(pctx, " "); 1411 cfg_print_cstr(pctx, "<quoted_string>"); 1412 } 1413 #endif /* HAVE_GEOIP */ 1414 1415 static cfg_type_t cfg_type_addrmatchelt; 1416 static cfg_type_t cfg_type_negated; 1417 1418 static isc_result_t 1419 parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, 1420 cfg_obj_t **ret) 1421 { 1422 isc_result_t result; 1423 UNUSED(type); 1424 1425 CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); 1426 1427 if (pctx->token.type == isc_tokentype_string || 1428 pctx->token.type == isc_tokentype_qstring) { 1429 if (pctx->token.type == isc_tokentype_string && 1430 (strcasecmp(TOKEN_STRING(pctx), "key") == 0)) { 1431 CHECK(cfg_parse_obj(pctx, &cfg_type_keyref, ret)); 1432 } else if (pctx->token.type == isc_tokentype_string && 1433 (strcasecmp(TOKEN_STRING(pctx), "geoip") == 0)) { 1434 #ifdef HAVE_GEOIP 1435 CHECK(cfg_gettoken(pctx, 0)); 1436 CHECK(cfg_parse_obj(pctx, &cfg_type_geoip, ret)); 1437 #else 1438 cfg_parser_error(pctx, CFG_LOG_NEAR, "'geoip' " 1439 "not supported in this build"); 1440 return (ISC_R_UNEXPECTEDTOKEN); 1441 #endif 1442 } else { 1443 if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | 1444 CFG_ADDR_V4PREFIXOK | 1445 CFG_ADDR_V6OK)) 1446 { 1447 CHECK(cfg_parse_netprefix(pctx, NULL, ret)); 1448 } else { 1449 CHECK(cfg_parse_astring(pctx, NULL, ret)); 1450 } 1451 } 1452 } else if (pctx->token.type == isc_tokentype_special) { 1453 if (pctx->token.value.as_char == '{') { 1454 /* Nested match list. */ 1455 CHECK(cfg_parse_obj(pctx, 1456 &cfg_type_bracketed_aml, ret)); 1457 } else if (pctx->token.value.as_char == '!') { 1458 CHECK(cfg_gettoken(pctx, 0)); /* read "!" */ 1459 CHECK(cfg_parse_obj(pctx, &cfg_type_negated, ret)); 1460 } else { 1461 goto bad; 1462 } 1463 } else { 1464 bad: 1465 cfg_parser_error(pctx, CFG_LOG_NEAR, 1466 "expected IP match list element"); 1467 return (ISC_R_UNEXPECTEDTOKEN); 1468 } 1469 cleanup: 1470 return (result); 1471 } 1472 1473 /*% 1474 * A negated address match list element (like "! 10.0.0.1"). 1475 * Somewhat sneakily, the caller is expected to parse the 1476 * "!", but not to print it. 1477 */ 1478 static cfg_tuplefielddef_t negated_fields[] = { 1479 { "negated", &cfg_type_addrmatchelt, 0 }, 1480 { NULL, NULL, 0 } 1481 }; 1482 1483 static void 1484 print_negated(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1485 cfg_print_cstr(pctx, "!"); 1486 cfg_print_tuple(pctx, obj); 1487 } 1488 1489 static cfg_type_t cfg_type_negated = { 1490 "negated", cfg_parse_tuple, print_negated, NULL, &cfg_rep_tuple, 1491 &negated_fields 1492 }; 1493 1494 /*% An address match list element */ 1495 1496 static cfg_type_t cfg_type_addrmatchelt = { 1497 "address_match_element", parse_addrmatchelt, NULL, cfg_doc_terminal, 1498 NULL, NULL 1499 }; 1500 1501 /*% 1502 * A bracketed address match list 1503 */ 1504 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_bracketed_aml = { 1505 "bracketed_aml", cfg_parse_bracketed_list, cfg_print_bracketed_list, 1506 cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_addrmatchelt 1507 }; 1508 1509 /* 1510 * Optional bracketed text 1511 */ 1512 static isc_result_t 1513 parse_optional_btext(cfg_parser_t *pctx, const cfg_type_t *type, 1514 cfg_obj_t **ret) 1515 { 1516 isc_result_t result; 1517 1518 UNUSED(type); 1519 1520 CHECK(cfg_peektoken(pctx, ISC_LEXOPT_BTEXT)); 1521 if (pctx->token.type == isc_tokentype_btext) { 1522 CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_text, ret)); 1523 } else { 1524 CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret)); 1525 } 1526 cleanup: 1527 return (result); 1528 } 1529 1530 static void 1531 print_optional_btext(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1532 if (obj->type == &cfg_type_void) { 1533 return; 1534 } 1535 1536 pctx->indent++; 1537 cfg_print_cstr(pctx, "{"); 1538 cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length); 1539 print_close(pctx); 1540 } 1541 1542 static void 1543 doc_optional_btext(cfg_printer_t *pctx, const cfg_type_t *type) { 1544 UNUSED(type); 1545 1546 cfg_print_cstr(pctx, "[ { <unspecified-text> } ]"); 1547 } 1548 1549 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_optional_bracketed_text = { 1550 "optional_btext", parse_optional_btext, print_optional_btext, 1551 doc_optional_btext, NULL, NULL 1552 }; 1553 1554 /* 1555 * Booleans 1556 */ 1557 1558 bool 1559 cfg_obj_isboolean(const cfg_obj_t *obj) { 1560 REQUIRE(obj != NULL); 1561 return (obj->type->rep == &cfg_rep_boolean); 1562 } 1563 1564 bool 1565 cfg_obj_asboolean(const cfg_obj_t *obj) { 1566 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean); 1567 return (obj->value.boolean); 1568 } 1569 1570 isc_result_t 1571 cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) 1572 { 1573 isc_result_t result; 1574 bool value; 1575 cfg_obj_t *obj = NULL; 1576 1577 REQUIRE(pctx != NULL); 1578 REQUIRE(ret != NULL && *ret == NULL); 1579 1580 UNUSED(type); 1581 1582 result = cfg_gettoken(pctx, 0); 1583 if (result != ISC_R_SUCCESS) 1584 return (result); 1585 1586 if (pctx->token.type != isc_tokentype_string) 1587 goto bad_boolean; 1588 1589 if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) || 1590 (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) || 1591 (strcmp(TOKEN_STRING(pctx), "1") == 0)) { 1592 value = true; 1593 } else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) || 1594 (strcasecmp(TOKEN_STRING(pctx), "no") == 0) || 1595 (strcmp(TOKEN_STRING(pctx), "0") == 0)) { 1596 value = false; 1597 } else { 1598 goto bad_boolean; 1599 } 1600 1601 CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj)); 1602 obj->value.boolean = value; 1603 *ret = obj; 1604 return (result); 1605 1606 bad_boolean: 1607 cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected"); 1608 return (ISC_R_UNEXPECTEDTOKEN); 1609 1610 cleanup: 1611 return (result); 1612 } 1613 1614 void 1615 cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1616 REQUIRE(pctx != NULL); 1617 REQUIRE(obj != NULL); 1618 1619 if (obj->value.boolean) 1620 cfg_print_cstr(pctx, "yes"); 1621 else 1622 cfg_print_cstr(pctx, "no"); 1623 } 1624 1625 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_boolean = { 1626 "boolean", cfg_parse_boolean, cfg_print_boolean, cfg_doc_terminal, 1627 &cfg_rep_boolean, NULL 1628 }; 1629 1630 /* 1631 * Lists. 1632 */ 1633 1634 isc_result_t 1635 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) { 1636 isc_result_t result; 1637 1638 REQUIRE(pctx != NULL); 1639 REQUIRE(type != NULL); 1640 REQUIRE(obj != NULL && *obj == NULL); 1641 1642 CHECK(cfg_create_obj(pctx, type, obj)); 1643 ISC_LIST_INIT((*obj)->value.list); 1644 cleanup: 1645 return (result); 1646 } 1647 1648 static isc_result_t 1649 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) { 1650 cfg_listelt_t *elt; 1651 1652 elt = isc_mem_get(pctx->mctx, sizeof(*elt)); 1653 if (elt == NULL) 1654 return (ISC_R_NOMEMORY); 1655 elt->obj = NULL; 1656 ISC_LINK_INIT(elt, link); 1657 *eltp = elt; 1658 return (ISC_R_SUCCESS); 1659 } 1660 1661 static void 1662 free_listelt(cfg_parser_t *pctx, cfg_listelt_t *elt) { 1663 if (elt->obj != NULL) 1664 cfg_obj_destroy(pctx, &elt->obj); 1665 isc_mem_put(pctx->mctx, elt, sizeof(*elt)); 1666 } 1667 1668 static void 1669 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) { 1670 cfg_listelt_t *elt, *next; 1671 for (elt = ISC_LIST_HEAD(obj->value.list); 1672 elt != NULL; 1673 elt = next) 1674 { 1675 next = ISC_LIST_NEXT(elt, link); 1676 free_listelt(pctx, elt); 1677 } 1678 } 1679 1680 isc_result_t 1681 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype, 1682 cfg_listelt_t **ret) 1683 { 1684 isc_result_t result; 1685 cfg_listelt_t *elt = NULL; 1686 cfg_obj_t *value = NULL; 1687 1688 REQUIRE(pctx != NULL); 1689 REQUIRE(elttype != NULL); 1690 REQUIRE(ret != NULL && *ret == NULL); 1691 1692 CHECK(create_listelt(pctx, &elt)); 1693 1694 result = cfg_parse_obj(pctx, elttype, &value); 1695 if (result != ISC_R_SUCCESS) 1696 goto cleanup; 1697 1698 elt->obj = value; 1699 1700 *ret = elt; 1701 return (ISC_R_SUCCESS); 1702 1703 cleanup: 1704 isc_mem_put(pctx->mctx, elt, sizeof(*elt)); 1705 return (result); 1706 } 1707 1708 /* 1709 * Parse a homogeneous list whose elements are of type 'elttype' 1710 * and where each element is terminated by a semicolon. 1711 */ 1712 static isc_result_t 1713 parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret) 1714 { 1715 cfg_obj_t *listobj = NULL; 1716 const cfg_type_t *listof = listtype->of; 1717 isc_result_t result; 1718 cfg_listelt_t *elt = NULL; 1719 1720 CHECK(cfg_create_list(pctx, listtype, &listobj)); 1721 1722 for (;;) { 1723 CHECK(cfg_peektoken(pctx, 0)); 1724 if (pctx->token.type == isc_tokentype_special && 1725 pctx->token.value.as_char == /*{*/ '}') 1726 break; 1727 CHECK(cfg_parse_listelt(pctx, listof, &elt)); 1728 CHECK(parse_semicolon(pctx)); 1729 ISC_LIST_APPEND(listobj->value.list, elt, link); 1730 elt = NULL; 1731 } 1732 *ret = listobj; 1733 return (ISC_R_SUCCESS); 1734 1735 cleanup: 1736 if (elt != NULL) 1737 free_listelt(pctx, elt); 1738 CLEANUP_OBJ(listobj); 1739 return (result); 1740 } 1741 1742 static void 1743 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1744 const cfg_list_t *list = &obj->value.list; 1745 const cfg_listelt_t *elt; 1746 1747 for (elt = ISC_LIST_HEAD(*list); 1748 elt != NULL; 1749 elt = ISC_LIST_NEXT(elt, link)) 1750 { 1751 if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) { 1752 cfg_print_obj(pctx, elt->obj); 1753 cfg_print_cstr(pctx, "; "); 1754 } else { 1755 cfg_print_indent(pctx); 1756 cfg_print_obj(pctx, elt->obj); 1757 cfg_print_cstr(pctx, ";\n"); 1758 } 1759 } 1760 } 1761 1762 isc_result_t 1763 cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type, 1764 cfg_obj_t **ret) 1765 { 1766 isc_result_t result; 1767 1768 REQUIRE(pctx != NULL); 1769 REQUIRE(type != NULL); 1770 REQUIRE(ret != NULL && *ret == NULL); 1771 1772 CHECK(cfg_parse_special(pctx, '{')); 1773 CHECK(parse_list(pctx, type, ret)); 1774 CHECK(cfg_parse_special(pctx, '}')); 1775 cleanup: 1776 return (result); 1777 } 1778 1779 void 1780 cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1781 REQUIRE(pctx != NULL); 1782 REQUIRE(obj != NULL); 1783 1784 print_open(pctx); 1785 print_list(pctx, obj); 1786 print_close(pctx); 1787 } 1788 1789 void 1790 cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) { 1791 REQUIRE(pctx != NULL); 1792 REQUIRE(type != NULL); 1793 1794 cfg_print_cstr(pctx, "{ "); 1795 cfg_doc_obj(pctx, type->of); 1796 cfg_print_cstr(pctx, "; ... }"); 1797 } 1798 1799 /* 1800 * Parse a homogeneous list whose elements are of type 'elttype' 1801 * and where elements are separated by space. The list ends 1802 * before the first semicolon. 1803 */ 1804 isc_result_t 1805 cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype, 1806 cfg_obj_t **ret) 1807 { 1808 cfg_obj_t *listobj = NULL; 1809 const cfg_type_t *listof = listtype->of; 1810 isc_result_t result; 1811 1812 REQUIRE(pctx != NULL); 1813 REQUIRE(listtype != NULL); 1814 REQUIRE(ret != NULL && *ret == NULL); 1815 1816 CHECK(cfg_create_list(pctx, listtype, &listobj)); 1817 1818 for (;;) { 1819 cfg_listelt_t *elt = NULL; 1820 1821 CHECK(cfg_peektoken(pctx, 0)); 1822 if (pctx->token.type == isc_tokentype_special && 1823 pctx->token.value.as_char == ';') 1824 break; 1825 CHECK(cfg_parse_listelt(pctx, listof, &elt)); 1826 ISC_LIST_APPEND(listobj->value.list, elt, link); 1827 } 1828 *ret = listobj; 1829 return (ISC_R_SUCCESS); 1830 1831 cleanup: 1832 CLEANUP_OBJ(listobj); 1833 return (result); 1834 } 1835 1836 void 1837 cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1838 const cfg_list_t *list = &obj->value.list; 1839 const cfg_listelt_t *elt; 1840 1841 REQUIRE(pctx != NULL); 1842 REQUIRE(obj != NULL); 1843 1844 for (elt = ISC_LIST_HEAD(*list); 1845 elt != NULL; 1846 elt = ISC_LIST_NEXT(elt, link)) { 1847 cfg_print_obj(pctx, elt->obj); 1848 if (ISC_LIST_NEXT(elt, link) != NULL) 1849 cfg_print_cstr(pctx, " "); 1850 } 1851 } 1852 1853 bool 1854 cfg_obj_islist(const cfg_obj_t *obj) { 1855 REQUIRE(obj != NULL); 1856 return (obj->type->rep == &cfg_rep_list); 1857 } 1858 1859 const cfg_listelt_t * 1860 cfg_list_first(const cfg_obj_t *obj) { 1861 REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list); 1862 if (obj == NULL) 1863 return (NULL); 1864 return (ISC_LIST_HEAD(obj->value.list)); 1865 } 1866 1867 const cfg_listelt_t * 1868 cfg_list_next(const cfg_listelt_t *elt) { 1869 REQUIRE(elt != NULL); 1870 return (ISC_LIST_NEXT(elt, link)); 1871 } 1872 1873 /* 1874 * Return the length of a list object. If obj is NULL or is not 1875 * a list, return 0. 1876 */ 1877 unsigned int 1878 cfg_list_length(const cfg_obj_t *obj, bool recurse) { 1879 const cfg_listelt_t *elt; 1880 unsigned int count = 0; 1881 1882 if (obj == NULL || !cfg_obj_islist(obj)) 1883 return (0U); 1884 for (elt = cfg_list_first(obj); 1885 elt != NULL; 1886 elt = cfg_list_next(elt)) { 1887 if (recurse && cfg_obj_islist(elt->obj)) { 1888 count += cfg_list_length(elt->obj, recurse); 1889 } else { 1890 count++; 1891 } 1892 } 1893 return (count); 1894 } 1895 1896 cfg_obj_t * 1897 cfg_listelt_value(const cfg_listelt_t *elt) { 1898 REQUIRE(elt != NULL); 1899 return (elt->obj); 1900 } 1901 1902 /* 1903 * Maps. 1904 */ 1905 1906 /* 1907 * Parse a map body. That's something like 1908 * 1909 * "foo 1; bar { glub; }; zap true; zap false;" 1910 * 1911 * i.e., a sequence of option names followed by values and 1912 * terminated by semicolons. Used for the top level of 1913 * the named.conf syntax, as well as for the body of the 1914 * options, view, zone, and other statements. 1915 */ 1916 isc_result_t 1917 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) 1918 { 1919 const cfg_clausedef_t * const *clausesets = type->of; 1920 isc_result_t result; 1921 const cfg_clausedef_t * const *clauseset; 1922 const cfg_clausedef_t *clause; 1923 cfg_obj_t *value = NULL; 1924 cfg_obj_t *obj = NULL; 1925 cfg_obj_t *eltobj = NULL; 1926 cfg_obj_t *includename = NULL; 1927 isc_symvalue_t symval; 1928 cfg_list_t *list = NULL; 1929 1930 REQUIRE(pctx != NULL); 1931 REQUIRE(type != NULL); 1932 REQUIRE(ret != NULL && *ret == NULL); 1933 1934 CHECK(create_map(pctx, type, &obj)); 1935 1936 obj->value.map.clausesets = clausesets; 1937 1938 for (;;) { 1939 cfg_listelt_t *elt; 1940 1941 redo: 1942 /* 1943 * Parse the option name and see if it is known. 1944 */ 1945 CHECK(cfg_gettoken(pctx, 0)); 1946 1947 if (pctx->token.type != isc_tokentype_string) { 1948 cfg_ungettoken(pctx); 1949 break; 1950 } 1951 1952 /* 1953 * We accept "include" statements wherever a map body 1954 * clause can occur. 1955 */ 1956 if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) { 1957 /* 1958 * Turn the file name into a temporary configuration 1959 * object just so that it is not overwritten by the 1960 * semicolon token. 1961 */ 1962 CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename)); 1963 CHECK(parse_semicolon(pctx)); 1964 CHECK(parser_openfile(pctx, includename-> 1965 value.string.base)); 1966 cfg_obj_destroy(pctx, &includename); 1967 goto redo; 1968 } 1969 1970 clause = NULL; 1971 for (clauseset = clausesets; *clauseset != NULL; clauseset++) { 1972 for (clause = *clauseset; 1973 clause->name != NULL; 1974 clause++) { 1975 if (strcasecmp(TOKEN_STRING(pctx), 1976 clause->name) == 0) 1977 goto done; 1978 } 1979 } 1980 done: 1981 if (clause == NULL || clause->name == NULL) { 1982 cfg_parser_error(pctx, CFG_LOG_NOPREP, 1983 "unknown option"); 1984 /* 1985 * Try to recover by parsing this option as an unknown 1986 * option and discarding it. 1987 */ 1988 CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, 1989 &eltobj)); 1990 cfg_obj_destroy(pctx, &eltobj); 1991 CHECK(parse_semicolon(pctx)); 1992 continue; 1993 } 1994 1995 /* Clause is known. */ 1996 1997 /* Issue fatal errors if appropriate */ 1998 if ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0) { 1999 cfg_parser_error(pctx, 0, 2000 "option '%s' no longer exists", 2001 clause->name); 2002 CHECK(ISC_R_FAILURE); 2003 } 2004 2005 /* Issue warnings if appropriate */ 2006 if ((pctx->flags & CFG_PCTX_NODEPRECATED) == 0 && 2007 (clause->flags & CFG_CLAUSEFLAG_DEPRECATED) != 0) 2008 { 2009 cfg_parser_warning(pctx, 0, 2010 "option '%s' is deprecated", 2011 clause->name); 2012 } 2013 if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) { 2014 cfg_parser_warning(pctx, 0, 2015 "option '%s' is obsolete and " 2016 "should be removed ", 2017 clause->name); 2018 } 2019 if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0) { 2020 cfg_parser_warning(pctx, 0, 2021 "option '%s' is not implemented", 2022 clause->name); 2023 } 2024 if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0) { 2025 cfg_parser_warning(pctx, 0, 2026 "option '%s' is not implemented", 2027 clause->name); 2028 } 2029 if ((clause->flags & CFG_CLAUSEFLAG_NOOP) != 0) { 2030 cfg_parser_warning(pctx, 0, "option '%s' was not " 2031 "enabled at compile time " 2032 "(ignored)", clause->name); 2033 } 2034 2035 if ((clause->flags & CFG_CLAUSEFLAG_NOTCONFIGURED) != 0) { 2036 cfg_parser_error(pctx, 0, "option '%s' was not " 2037 "enabled at compile time", 2038 clause->name); 2039 CHECK(ISC_R_FAILURE); 2040 } 2041 2042 /* 2043 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT 2044 * set here - we need to log the *lack* of such an option, 2045 * not its presence. 2046 */ 2047 2048 /* See if the clause already has a value; if not create one. */ 2049 result = isc_symtab_lookup(obj->value.map.symtab, 2050 clause->name, 0, &symval); 2051 2052 if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) { 2053 /* Multivalued clause */ 2054 cfg_obj_t *listobj = NULL; 2055 if (result == ISC_R_NOTFOUND) { 2056 CHECK(cfg_create_list(pctx, 2057 &cfg_type_implicitlist, 2058 &listobj)); 2059 symval.as_pointer = listobj; 2060 result = isc_symtab_define(obj->value. 2061 map.symtab, 2062 clause->name, 2063 1, symval, 2064 isc_symexists_reject); 2065 if (result != ISC_R_SUCCESS) { 2066 cfg_parser_error(pctx, CFG_LOG_NEAR, 2067 "isc_symtab_define(%s) " 2068 "failed", clause->name); 2069 isc_mem_put(pctx->mctx, list, 2070 sizeof(cfg_list_t)); 2071 goto cleanup; 2072 } 2073 } else { 2074 INSIST(result == ISC_R_SUCCESS); 2075 listobj = symval.as_pointer; 2076 } 2077 2078 elt = NULL; 2079 CHECK(cfg_parse_listelt(pctx, clause->type, &elt)); 2080 CHECK(parse_semicolon(pctx)); 2081 2082 ISC_LIST_APPEND(listobj->value.list, elt, link); 2083 } else { 2084 /* Single-valued clause */ 2085 if (result == ISC_R_NOTFOUND) { 2086 bool callback = 2087 ((clause->flags & 2088 CFG_CLAUSEFLAG_CALLBACK) != 0); 2089 CHECK(parse_symtab_elt(pctx, clause->name, 2090 clause->type, 2091 obj->value.map.symtab, 2092 callback)); 2093 CHECK(parse_semicolon(pctx)); 2094 } else if (result == ISC_R_SUCCESS) { 2095 cfg_parser_error(pctx, CFG_LOG_NEAR, 2096 "'%s' redefined", 2097 clause->name); 2098 result = ISC_R_EXISTS; 2099 goto cleanup; 2100 } else { 2101 cfg_parser_error(pctx, CFG_LOG_NEAR, 2102 "isc_symtab_define() failed"); 2103 goto cleanup; 2104 } 2105 } 2106 } 2107 2108 2109 *ret = obj; 2110 return (ISC_R_SUCCESS); 2111 2112 cleanup: 2113 CLEANUP_OBJ(value); 2114 CLEANUP_OBJ(obj); 2115 CLEANUP_OBJ(eltobj); 2116 CLEANUP_OBJ(includename); 2117 return (result); 2118 } 2119 2120 static isc_result_t 2121 parse_symtab_elt(cfg_parser_t *pctx, const char *name, 2122 cfg_type_t *elttype, isc_symtab_t *symtab, 2123 bool callback) 2124 { 2125 isc_result_t result; 2126 cfg_obj_t *obj = NULL; 2127 isc_symvalue_t symval; 2128 2129 CHECK(cfg_parse_obj(pctx, elttype, &obj)); 2130 2131 if (callback && pctx->callback != NULL) 2132 CHECK(pctx->callback(name, obj, pctx->callbackarg)); 2133 2134 symval.as_pointer = obj; 2135 CHECK(isc_symtab_define(symtab, name, 2136 1, symval, 2137 isc_symexists_reject)); 2138 return (ISC_R_SUCCESS); 2139 2140 cleanup: 2141 CLEANUP_OBJ(obj); 2142 return (result); 2143 } 2144 2145 /* 2146 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }" 2147 */ 2148 isc_result_t 2149 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 2150 isc_result_t result; 2151 2152 REQUIRE(pctx != NULL); 2153 REQUIRE(type != NULL); 2154 REQUIRE(ret != NULL && *ret == NULL); 2155 2156 CHECK(cfg_parse_special(pctx, '{')); 2157 CHECK(cfg_parse_mapbody(pctx, type, ret)); 2158 CHECK(cfg_parse_special(pctx, '}')); 2159 cleanup: 2160 return (result); 2161 } 2162 2163 /* 2164 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map(). 2165 */ 2166 static isc_result_t 2167 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, 2168 const cfg_type_t *type, cfg_obj_t **ret) 2169 { 2170 isc_result_t result; 2171 cfg_obj_t *idobj = NULL; 2172 cfg_obj_t *mapobj = NULL; 2173 2174 REQUIRE(pctx != NULL); 2175 REQUIRE(nametype != NULL); 2176 REQUIRE(type != NULL); 2177 REQUIRE(ret != NULL && *ret == NULL); 2178 2179 CHECK(cfg_parse_obj(pctx, nametype, &idobj)); 2180 CHECK(cfg_parse_map(pctx, type, &mapobj)); 2181 mapobj->value.map.id = idobj; 2182 *ret = mapobj; 2183 return (result); 2184 cleanup: 2185 CLEANUP_OBJ(idobj); 2186 CLEANUP_OBJ(mapobj); 2187 return (result); 2188 } 2189 2190 /* 2191 * Parse a map identified by a string name. E.g., "name { foo 1; }". 2192 * Used for the "key" and "channel" statements. 2193 */ 2194 isc_result_t 2195 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 2196 return (parse_any_named_map(pctx, &cfg_type_astring, type, ret)); 2197 } 2198 2199 /* 2200 * Parse a map identified by a network address. 2201 * Used to be used for the "server" statement. 2202 */ 2203 isc_result_t 2204 cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 2205 return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret)); 2206 } 2207 2208 /* 2209 * Parse a map identified by a network prefix. 2210 * Used for the "server" statement. 2211 */ 2212 isc_result_t 2213 cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 2214 return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret)); 2215 } 2216 2217 static void 2218 print_symval(cfg_printer_t *pctx, const char *name, cfg_obj_t *obj) { 2219 if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) 2220 cfg_print_indent(pctx); 2221 2222 cfg_print_cstr(pctx, name); 2223 cfg_print_cstr(pctx, " "); 2224 cfg_print_obj(pctx, obj); 2225 2226 if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) 2227 cfg_print_cstr(pctx, ";\n"); 2228 else 2229 cfg_print_cstr(pctx, "; "); 2230 } 2231 2232 void 2233 cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) { 2234 isc_result_t result = ISC_R_SUCCESS; 2235 const cfg_clausedef_t * const *clauseset; 2236 2237 REQUIRE(pctx != NULL); 2238 REQUIRE(obj != NULL); 2239 2240 for (clauseset = obj->value.map.clausesets; 2241 *clauseset != NULL; 2242 clauseset++) 2243 { 2244 isc_symvalue_t symval; 2245 const cfg_clausedef_t *clause; 2246 2247 for (clause = *clauseset; 2248 clause->name != NULL; 2249 clause++) { 2250 result = isc_symtab_lookup(obj->value.map.symtab, 2251 clause->name, 0, &symval); 2252 if (result == ISC_R_SUCCESS) { 2253 cfg_obj_t *symobj = symval.as_pointer; 2254 if (symobj->type == &cfg_type_implicitlist) { 2255 /* Multivalued. */ 2256 cfg_list_t *list = &symobj->value.list; 2257 cfg_listelt_t *elt; 2258 for (elt = ISC_LIST_HEAD(*list); 2259 elt != NULL; 2260 elt = ISC_LIST_NEXT(elt, link)) { 2261 print_symval(pctx, 2262 clause->name, 2263 elt->obj); 2264 } 2265 } else { 2266 /* Single-valued. */ 2267 print_symval(pctx, clause->name, 2268 symobj); 2269 } 2270 } else if (result == ISC_R_NOTFOUND) { 2271 ; /* do nothing */ 2272 } else { 2273 INSIST(0); 2274 ISC_UNREACHABLE(); 2275 } 2276 } 2277 } 2278 } 2279 2280 static struct flagtext { 2281 unsigned int flag; 2282 const char *text; 2283 } flagtexts[] = { 2284 { CFG_CLAUSEFLAG_NOTIMP, "not implemented" }, 2285 { CFG_CLAUSEFLAG_NYI, "not yet implemented" }, 2286 { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" }, 2287 { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" }, 2288 { CFG_CLAUSEFLAG_TESTONLY, "test only" }, 2289 { CFG_CLAUSEFLAG_NOTCONFIGURED, "not configured" }, 2290 { CFG_CLAUSEFLAG_MULTI, "may occur multiple times" }, 2291 { CFG_CLAUSEFLAG_EXPERIMENTAL, "experimental" }, 2292 { CFG_CLAUSEFLAG_NOOP, "non-operational" }, 2293 { CFG_CLAUSEFLAG_DEPRECATED, "deprecated" }, 2294 { CFG_CLAUSEFLAG_ANCIENT, "ancient" }, 2295 { 0, NULL } 2296 }; 2297 2298 void 2299 cfg_print_clauseflags(cfg_printer_t *pctx, unsigned int flags) { 2300 struct flagtext *p; 2301 bool first = true; 2302 for (p = flagtexts; p->flag != 0; p++) { 2303 if ((flags & p->flag) != 0) { 2304 if (first) 2305 cfg_print_cstr(pctx, " // "); 2306 else 2307 cfg_print_cstr(pctx, ", "); 2308 cfg_print_cstr(pctx, p->text); 2309 first = false; 2310 } 2311 } 2312 } 2313 2314 void 2315 cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) { 2316 const cfg_clausedef_t * const *clauseset; 2317 const cfg_clausedef_t *clause; 2318 2319 REQUIRE(pctx != NULL); 2320 REQUIRE(type != NULL); 2321 2322 for (clauseset = type->of; *clauseset != NULL; clauseset++) { 2323 for (clause = *clauseset; clause->name != NULL; clause++) { 2324 cfg_print_cstr(pctx, clause->name); 2325 cfg_print_cstr(pctx, " "); 2326 cfg_doc_obj(pctx, clause->type); 2327 cfg_print_cstr(pctx, ";"); 2328 cfg_print_clauseflags(pctx, clause->flags); 2329 cfg_print_cstr(pctx, "\n\n"); 2330 } 2331 } 2332 } 2333 2334 void 2335 cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) { 2336 REQUIRE(pctx != NULL); 2337 REQUIRE(obj != NULL); 2338 2339 if (obj->value.map.id != NULL) { 2340 cfg_print_obj(pctx, obj->value.map.id); 2341 cfg_print_cstr(pctx, " "); 2342 } 2343 print_open(pctx); 2344 cfg_print_mapbody(pctx, obj); 2345 print_close(pctx); 2346 } 2347 2348 void 2349 cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) { 2350 const cfg_clausedef_t * const *clauseset; 2351 const cfg_clausedef_t *clause; 2352 2353 REQUIRE(pctx != NULL); 2354 REQUIRE(type != NULL); 2355 2356 if (type->parse == cfg_parse_named_map) { 2357 cfg_doc_obj(pctx, &cfg_type_astring); 2358 cfg_print_cstr(pctx, " "); 2359 } else if (type->parse == cfg_parse_addressed_map) { 2360 cfg_doc_obj(pctx, &cfg_type_netaddr); 2361 cfg_print_cstr(pctx, " "); 2362 } else if (type->parse == cfg_parse_netprefix_map) { 2363 cfg_doc_obj(pctx, &cfg_type_netprefix); 2364 cfg_print_cstr(pctx, " "); 2365 } 2366 2367 print_open(pctx); 2368 2369 for (clauseset = type->of; *clauseset != NULL; clauseset++) { 2370 for (clause = *clauseset; clause->name != NULL; clause++) { 2371 cfg_print_indent(pctx); 2372 cfg_print_cstr(pctx, clause->name); 2373 if (clause->type->print != cfg_print_void) 2374 cfg_print_cstr(pctx, " "); 2375 cfg_doc_obj(pctx, clause->type); 2376 cfg_print_cstr(pctx, ";"); 2377 cfg_print_clauseflags(pctx, clause->flags); 2378 cfg_print_cstr(pctx, "\n"); 2379 } 2380 } 2381 print_close(pctx); 2382 } 2383 2384 bool 2385 cfg_obj_ismap(const cfg_obj_t *obj) { 2386 REQUIRE(obj != NULL); 2387 return (obj->type->rep == &cfg_rep_map); 2388 } 2389 2390 isc_result_t 2391 cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) { 2392 isc_result_t result; 2393 isc_symvalue_t val; 2394 const cfg_map_t *map; 2395 2396 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 2397 REQUIRE(name != NULL); 2398 REQUIRE(obj != NULL && *obj == NULL); 2399 2400 map = &mapobj->value.map; 2401 2402 result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val); 2403 if (result != ISC_R_SUCCESS) 2404 return (result); 2405 *obj = val.as_pointer; 2406 return (ISC_R_SUCCESS); 2407 } 2408 2409 const cfg_obj_t * 2410 cfg_map_getname(const cfg_obj_t *mapobj) { 2411 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 2412 return (mapobj->value.map.id); 2413 } 2414 2415 unsigned int 2416 cfg_map_count(const cfg_obj_t *mapobj) { 2417 const cfg_map_t *map; 2418 2419 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 2420 2421 map = &mapobj->value.map; 2422 return (isc_symtab_count(map->symtab)); 2423 } 2424 2425 const char * 2426 cfg_map_firstclause(const cfg_type_t *map, const void **clauses, 2427 unsigned int *idx) 2428 { 2429 cfg_clausedef_t * const * clauseset; 2430 2431 REQUIRE(map != NULL && map->rep == &cfg_rep_map); 2432 REQUIRE(idx != NULL); 2433 REQUIRE(clauses != NULL && *clauses == NULL); 2434 2435 clauseset = map->of; 2436 if (*clauseset == NULL) { 2437 return (NULL); 2438 } 2439 *clauses = *clauseset; 2440 *idx = 0; 2441 while ((*clauseset)[*idx].name == NULL) { 2442 *clauses = (*++clauseset); 2443 if (*clauses == NULL) 2444 return (NULL); 2445 } 2446 return ((*clauseset)[*idx].name); 2447 } 2448 2449 const char * 2450 cfg_map_nextclause(const cfg_type_t *map, const void **clauses, 2451 unsigned int *idx) 2452 { 2453 cfg_clausedef_t * const * clauseset; 2454 2455 REQUIRE(map != NULL && map->rep == &cfg_rep_map); 2456 REQUIRE(idx != NULL); 2457 REQUIRE(clauses != NULL && *clauses != NULL); 2458 2459 clauseset = map->of; 2460 while (*clauseset != NULL && *clauseset != *clauses) { 2461 clauseset++; 2462 } 2463 INSIST(*clauseset == *clauses); 2464 (*idx)++; 2465 while ((*clauseset)[*idx].name == NULL) { 2466 *idx = 0; 2467 *clauses = (*++clauseset); 2468 if (*clauses == NULL) 2469 return (NULL); 2470 } 2471 return ((*clauseset)[*idx].name); 2472 } 2473 2474 /* Parse an arbitrary token, storing its raw text representation. */ 2475 static isc_result_t 2476 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 2477 cfg_obj_t *obj = NULL; 2478 isc_result_t result; 2479 isc_region_t r; 2480 2481 UNUSED(type); 2482 2483 CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj)); 2484 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); 2485 if (pctx->token.type == isc_tokentype_eof) { 2486 cfg_ungettoken(pctx); 2487 result = ISC_R_EOF; 2488 goto cleanup; 2489 } 2490 2491 isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r); 2492 2493 obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1); 2494 if (obj->value.string.base == NULL) { 2495 result = ISC_R_NOMEMORY; 2496 goto cleanup; 2497 } 2498 obj->value.string.length = r.length; 2499 memmove(obj->value.string.base, r.base, r.length); 2500 obj->value.string.base[r.length] = '\0'; 2501 *ret = obj; 2502 return (result); 2503 2504 cleanup: 2505 if (obj != NULL) 2506 isc_mem_put(pctx->mctx, obj, sizeof(*obj)); 2507 return (result); 2508 } 2509 2510 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_token = { 2511 "token", parse_token, cfg_print_ustring, cfg_doc_terminal, 2512 &cfg_rep_string, NULL 2513 }; 2514 2515 /* 2516 * An unsupported option. This is just a list of tokens with balanced braces 2517 * ending in a semicolon. 2518 */ 2519 2520 static isc_result_t 2521 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 2522 cfg_obj_t *listobj = NULL; 2523 isc_result_t result; 2524 int braces = 0; 2525 2526 CHECK(cfg_create_list(pctx, type, &listobj)); 2527 2528 for (;;) { 2529 cfg_listelt_t *elt = NULL; 2530 2531 CHECK(cfg_peektoken(pctx, 0)); 2532 if (pctx->token.type == isc_tokentype_special) { 2533 if (pctx->token.value.as_char == '{') 2534 braces++; 2535 else if (pctx->token.value.as_char == '}') 2536 braces--; 2537 else if (pctx->token.value.as_char == ';') 2538 if (braces == 0) 2539 break; 2540 } 2541 if (pctx->token.type == isc_tokentype_eof || braces < 0) { 2542 cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token"); 2543 result = ISC_R_UNEXPECTEDTOKEN; 2544 goto cleanup; 2545 } 2546 2547 CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt)); 2548 ISC_LIST_APPEND(listobj->value.list, elt, link); 2549 } 2550 INSIST(braces == 0); 2551 *ret = listobj; 2552 return (ISC_R_SUCCESS); 2553 2554 cleanup: 2555 CLEANUP_OBJ(listobj); 2556 return (result); 2557 } 2558 2559 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_unsupported = { 2560 "unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal, 2561 &cfg_rep_list, NULL 2562 }; 2563 2564 /* 2565 * Try interpreting the current token as a network address. 2566 * 2567 * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard 2568 * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set. The 2569 * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is 2570 * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set), 2571 * and the IPv6 wildcard address otherwise. 2572 */ 2573 static isc_result_t 2574 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) { 2575 char *s; 2576 struct in_addr in4a; 2577 struct in6_addr in6a; 2578 2579 if (pctx->token.type != isc_tokentype_string) 2580 return (ISC_R_UNEXPECTEDTOKEN); 2581 2582 s = TOKEN_STRING(pctx); 2583 if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) { 2584 if ((flags & CFG_ADDR_V4OK) != 0) { 2585 isc_netaddr_any(na); 2586 return (ISC_R_SUCCESS); 2587 } else if ((flags & CFG_ADDR_V6OK) != 0) { 2588 isc_netaddr_any6(na); 2589 return (ISC_R_SUCCESS); 2590 } else { 2591 INSIST(0); 2592 ISC_UNREACHABLE(); 2593 } 2594 } else { 2595 if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) { 2596 if (inet_pton(AF_INET, s, &in4a) == 1) { 2597 isc_netaddr_fromin(na, &in4a); 2598 return (ISC_R_SUCCESS); 2599 } 2600 } 2601 if ((flags & CFG_ADDR_V4PREFIXOK) != 0 && strlen(s) <= 15U) { 2602 char buf[64]; 2603 int i; 2604 2605 strlcpy(buf, s, sizeof(buf)); 2606 for (i = 0; i < 3; i++) { 2607 strlcat(buf, ".0", sizeof(buf)); 2608 if (inet_pton(AF_INET, buf, &in4a) == 1) { 2609 isc_netaddr_fromin(na, &in4a); 2610 return (ISC_R_SUCCESS); 2611 } 2612 } 2613 } 2614 if ((flags & CFG_ADDR_V6OK) != 0 && strlen(s) <= 127U) { 2615 char buf[128]; /* see lib/bind9/getaddresses.c */ 2616 char *d; /* zone delimiter */ 2617 uint32_t zone = 0; /* scope zone ID */ 2618 2619 strlcpy(buf, s, sizeof(buf)); 2620 d = strchr(buf, '%'); 2621 if (d != NULL) 2622 *d = '\0'; 2623 2624 if (inet_pton(AF_INET6, buf, &in6a) == 1) { 2625 if (d != NULL) { 2626 isc_result_t result; 2627 2628 result = isc_netscope_pton(AF_INET6, 2629 d + 1, 2630 &in6a, 2631 &zone); 2632 if (result != ISC_R_SUCCESS) 2633 return (result); 2634 } 2635 2636 isc_netaddr_fromin6(na, &in6a); 2637 isc_netaddr_setzone(na, zone); 2638 return (ISC_R_SUCCESS); 2639 } 2640 } 2641 } 2642 return (ISC_R_UNEXPECTEDTOKEN); 2643 } 2644 2645 isc_result_t 2646 cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) { 2647 isc_result_t result; 2648 const char *wild = ""; 2649 const char *prefix = ""; 2650 2651 REQUIRE(pctx != NULL); 2652 REQUIRE(na != NULL); 2653 2654 CHECK(cfg_gettoken(pctx, 0)); 2655 result = token_addr(pctx, flags, na); 2656 if (result == ISC_R_UNEXPECTEDTOKEN) { 2657 if ((flags & CFG_ADDR_WILDOK) != 0) 2658 wild = " or '*'"; 2659 if ((flags & CFG_ADDR_V4PREFIXOK) != 0) 2660 wild = " or IPv4 prefix"; 2661 if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK) 2662 cfg_parser_error(pctx, CFG_LOG_NEAR, 2663 "expected IPv4 address%s%s", 2664 prefix, wild); 2665 else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK) 2666 cfg_parser_error(pctx, CFG_LOG_NEAR, 2667 "expected IPv6 address%s%s", 2668 prefix, wild); 2669 else 2670 cfg_parser_error(pctx, CFG_LOG_NEAR, 2671 "expected IP address%s%s", 2672 prefix, wild); 2673 } 2674 cleanup: 2675 return (result); 2676 } 2677 2678 bool 2679 cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) { 2680 isc_result_t result; 2681 isc_netaddr_t na_dummy; 2682 2683 REQUIRE(pctx != NULL); 2684 2685 result = token_addr(pctx, flags, &na_dummy); 2686 return (result == ISC_R_SUCCESS); 2687 } 2688 2689 isc_result_t 2690 cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) { 2691 isc_result_t result; 2692 2693 REQUIRE(pctx != NULL); 2694 REQUIRE(port != NULL); 2695 2696 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER)); 2697 2698 if ((flags & CFG_ADDR_WILDOK) != 0 && 2699 pctx->token.type == isc_tokentype_string && 2700 strcmp(TOKEN_STRING(pctx), "*") == 0) { 2701 *port = 0; 2702 return (ISC_R_SUCCESS); 2703 } 2704 if (pctx->token.type != isc_tokentype_number) { 2705 cfg_parser_error(pctx, CFG_LOG_NEAR, 2706 "expected port number or '*'"); 2707 return (ISC_R_UNEXPECTEDTOKEN); 2708 } 2709 if (pctx->token.value.as_ulong >= 65536U) { 2710 cfg_parser_error(pctx, CFG_LOG_NEAR, 2711 "port number out of range"); 2712 return (ISC_R_UNEXPECTEDTOKEN); 2713 } 2714 *port = (in_port_t)(pctx->token.value.as_ulong); 2715 return (ISC_R_SUCCESS); 2716 cleanup: 2717 return (result); 2718 } 2719 2720 void 2721 cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) { 2722 isc_result_t result; 2723 char text[128]; 2724 isc_buffer_t buf; 2725 2726 REQUIRE(pctx != NULL); 2727 REQUIRE(na != NULL); 2728 2729 isc_buffer_init(&buf, text, sizeof(text)); 2730 result = isc_netaddr_totext(na, &buf); 2731 RUNTIME_CHECK(result == ISC_R_SUCCESS); 2732 cfg_print_chars(pctx, isc_buffer_base(&buf), 2733 isc_buffer_usedlength(&buf)); 2734 } 2735 2736 isc_result_t 2737 cfg_parse_dscp(cfg_parser_t *pctx, isc_dscp_t *dscp) { 2738 isc_result_t result; 2739 2740 REQUIRE(pctx != NULL); 2741 REQUIRE(dscp != NULL); 2742 2743 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER)); 2744 2745 if (pctx->token.type != isc_tokentype_number) { 2746 cfg_parser_error(pctx, CFG_LOG_NEAR, 2747 "expected number"); 2748 return (ISC_R_UNEXPECTEDTOKEN); 2749 } 2750 if (pctx->token.value.as_ulong > 63U) { 2751 cfg_parser_error(pctx, CFG_LOG_NEAR, 2752 "dscp out of range"); 2753 return (ISC_R_RANGE); 2754 } 2755 *dscp = (isc_dscp_t)(pctx->token.value.as_ulong); 2756 return (ISC_R_SUCCESS); 2757 cleanup: 2758 return (result); 2759 } 2760 2761 /* netaddr */ 2762 2763 static unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK; 2764 static unsigned int netaddr4_flags = CFG_ADDR_V4OK; 2765 static unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK; 2766 static unsigned int netaddr6_flags = CFG_ADDR_V6OK; 2767 static unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK; 2768 2769 static isc_result_t 2770 parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 2771 isc_result_t result; 2772 cfg_obj_t *obj = NULL; 2773 isc_netaddr_t netaddr; 2774 unsigned int flags = *(const unsigned int *)type->of; 2775 2776 CHECK(cfg_create_obj(pctx, type, &obj)); 2777 CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr)); 2778 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0); 2779 *ret = obj; 2780 return (ISC_R_SUCCESS); 2781 cleanup: 2782 CLEANUP_OBJ(obj); 2783 return (result); 2784 } 2785 2786 static void 2787 cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) { 2788 const unsigned int *flagp = type->of; 2789 int n = 0; 2790 if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK) { 2791 cfg_print_cstr(pctx, "( "); 2792 } 2793 if ((*flagp & CFG_ADDR_V4OK) != 0) { 2794 cfg_print_cstr(pctx, "<ipv4_address>"); 2795 n++; 2796 } 2797 if ((*flagp & CFG_ADDR_V6OK) != 0) { 2798 if (n != 0) 2799 cfg_print_cstr(pctx, " | "); 2800 cfg_print_cstr(pctx, "<ipv6_address>"); 2801 n++; 2802 } 2803 if ((*flagp & CFG_ADDR_WILDOK) != 0) { 2804 if (n != 0) 2805 cfg_print_cstr(pctx, " | "); 2806 cfg_print_cstr(pctx, "*"); 2807 n++; 2808 POST(n); 2809 } 2810 if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK) { 2811 cfg_print_cstr(pctx, " )"); 2812 } 2813 } 2814 2815 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_netaddr = { 2816 "netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr, 2817 &cfg_rep_sockaddr, &netaddr_flags 2818 }; 2819 2820 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_netaddr4 = { 2821 "netaddr4", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr, 2822 &cfg_rep_sockaddr, &netaddr4_flags 2823 }; 2824 2825 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_netaddr4wild = { 2826 "netaddr4wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr, 2827 &cfg_rep_sockaddr, &netaddr4wild_flags 2828 }; 2829 2830 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_netaddr6 = { 2831 "netaddr6", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr, 2832 &cfg_rep_sockaddr, &netaddr6_flags 2833 }; 2834 2835 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_netaddr6wild = { 2836 "netaddr6wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr, 2837 &cfg_rep_sockaddr, &netaddr6wild_flags 2838 }; 2839 2840 /* netprefix */ 2841 2842 isc_result_t 2843 cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type, 2844 cfg_obj_t **ret) 2845 { 2846 cfg_obj_t *obj = NULL; 2847 isc_result_t result; 2848 isc_netaddr_t netaddr; 2849 unsigned int addrlen = 0, prefixlen; 2850 2851 REQUIRE(pctx != NULL); 2852 REQUIRE(ret != NULL && *ret == NULL); 2853 2854 UNUSED(type); 2855 2856 CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK | 2857 CFG_ADDR_V6OK, &netaddr)); 2858 switch (netaddr.family) { 2859 case AF_INET: 2860 addrlen = 32; 2861 break; 2862 case AF_INET6: 2863 addrlen = 128; 2864 break; 2865 default: 2866 INSIST(0); 2867 ISC_UNREACHABLE(); 2868 } 2869 CHECK(cfg_peektoken(pctx, 0)); 2870 if (pctx->token.type == isc_tokentype_special && 2871 pctx->token.value.as_char == '/') { 2872 CHECK(cfg_gettoken(pctx, 0)); /* read "/" */ 2873 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER)); 2874 if (pctx->token.type != isc_tokentype_number) { 2875 cfg_parser_error(pctx, CFG_LOG_NEAR, 2876 "expected prefix length"); 2877 return (ISC_R_UNEXPECTEDTOKEN); 2878 } 2879 prefixlen = pctx->token.value.as_ulong; 2880 if (prefixlen > addrlen) { 2881 cfg_parser_error(pctx, CFG_LOG_NOPREP, 2882 "invalid prefix length"); 2883 return (ISC_R_RANGE); 2884 } 2885 } else { 2886 prefixlen = addrlen; 2887 } 2888 CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj)); 2889 obj->value.netprefix.address = netaddr; 2890 obj->value.netprefix.prefixlen = prefixlen; 2891 *ret = obj; 2892 return (ISC_R_SUCCESS); 2893 cleanup: 2894 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix"); 2895 return (result); 2896 } 2897 2898 static void 2899 print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) { 2900 const cfg_netprefix_t *p = &obj->value.netprefix; 2901 2902 cfg_print_rawaddr(pctx, &p->address); 2903 cfg_print_cstr(pctx, "/"); 2904 cfg_print_rawuint(pctx, p->prefixlen); 2905 } 2906 2907 bool 2908 cfg_obj_isnetprefix(const cfg_obj_t *obj) { 2909 REQUIRE(obj != NULL); 2910 return (obj->type->rep == &cfg_rep_netprefix); 2911 } 2912 2913 void 2914 cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr, 2915 unsigned int *prefixlen) 2916 { 2917 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix); 2918 REQUIRE(netaddr != NULL); 2919 REQUIRE(prefixlen != NULL); 2920 2921 *netaddr = obj->value.netprefix.address; 2922 *prefixlen = obj->value.netprefix.prefixlen; 2923 } 2924 2925 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_netprefix = { 2926 "netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal, 2927 &cfg_rep_netprefix, NULL 2928 }; 2929 2930 static isc_result_t 2931 parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type, 2932 int flags, cfg_obj_t **ret) 2933 { 2934 isc_result_t result; 2935 isc_netaddr_t netaddr; 2936 in_port_t port = 0; 2937 isc_dscp_t dscp = -1; 2938 cfg_obj_t *obj = NULL; 2939 int have_port = 0, have_dscp = 0; 2940 2941 CHECK(cfg_create_obj(pctx, type, &obj)); 2942 CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr)); 2943 for (;;) { 2944 CHECK(cfg_peektoken(pctx, 0)); 2945 if (pctx->token.type == isc_tokentype_string) { 2946 if (strcasecmp(TOKEN_STRING(pctx), "port") == 0) { 2947 CHECK(cfg_gettoken(pctx, 0)); /* read "port" */ 2948 CHECK(cfg_parse_rawport(pctx, flags, &port)); 2949 ++have_port; 2950 } else if ((flags & CFG_ADDR_DSCPOK) != 0 && 2951 strcasecmp(TOKEN_STRING(pctx), "dscp") == 0) 2952 { 2953 CHECK(cfg_gettoken(pctx, 0)); /* read "dscp" */ 2954 CHECK(cfg_parse_dscp(pctx, &dscp)); 2955 ++have_dscp; 2956 } else 2957 break; 2958 } else 2959 break; 2960 } 2961 if (have_port > 1) { 2962 cfg_parser_error(pctx, 0, "expected at most one port"); 2963 result = ISC_R_UNEXPECTEDTOKEN; 2964 goto cleanup; 2965 } 2966 2967 if (have_dscp > 1) { 2968 cfg_parser_error(pctx, 0, "expected at most one dscp"); 2969 result = ISC_R_UNEXPECTEDTOKEN; 2970 goto cleanup; 2971 } 2972 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port); 2973 obj->value.sockaddrdscp.dscp = dscp; 2974 *ret = obj; 2975 return (ISC_R_SUCCESS); 2976 2977 cleanup: 2978 CLEANUP_OBJ(obj); 2979 return (result); 2980 } 2981 2982 static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK; 2983 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_sockaddr = { 2984 "sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr, 2985 &cfg_rep_sockaddr, &sockaddr_flags 2986 }; 2987 2988 static unsigned int sockaddrdscp_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK | 2989 CFG_ADDR_DSCPOK; 2990 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_sockaddrdscp = { 2991 "sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr, 2992 &cfg_rep_sockaddr, &sockaddrdscp_flags 2993 }; 2994 2995 isc_result_t 2996 cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 2997 const unsigned int *flagp; 2998 2999 REQUIRE(pctx != NULL); 3000 REQUIRE(type != NULL); 3001 REQUIRE(ret != NULL && *ret == NULL); 3002 3003 flagp = type->of; 3004 3005 return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret)); 3006 } 3007 3008 void 3009 cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) { 3010 isc_netaddr_t netaddr; 3011 in_port_t port; 3012 char buf[ISC_NETADDR_FORMATSIZE]; 3013 3014 REQUIRE(pctx != NULL); 3015 REQUIRE(obj != NULL); 3016 3017 isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr); 3018 isc_netaddr_format(&netaddr, buf, sizeof(buf)); 3019 cfg_print_cstr(pctx, buf); 3020 port = isc_sockaddr_getport(&obj->value.sockaddr); 3021 if (port != 0) { 3022 cfg_print_cstr(pctx, " port "); 3023 cfg_print_rawuint(pctx, port); 3024 } 3025 if (obj->value.sockaddrdscp.dscp != -1) { 3026 cfg_print_cstr(pctx, " dscp "); 3027 cfg_print_rawuint(pctx, obj->value.sockaddrdscp.dscp); 3028 } 3029 } 3030 3031 void 3032 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) { 3033 const unsigned int *flagp = type->of; 3034 int n = 0; 3035 3036 REQUIRE(pctx != NULL); 3037 REQUIRE(type != NULL); 3038 3039 cfg_print_cstr(pctx, "( "); 3040 if ((*flagp & CFG_ADDR_V4OK) != 0) { 3041 cfg_print_cstr(pctx, "<ipv4_address>"); 3042 n++; 3043 } 3044 if ((*flagp & CFG_ADDR_V6OK) != 0) { 3045 if (n != 0) 3046 cfg_print_cstr(pctx, " | "); 3047 cfg_print_cstr(pctx, "<ipv6_address>"); 3048 n++; 3049 } 3050 if ((*flagp & CFG_ADDR_WILDOK) != 0) { 3051 if (n != 0) 3052 cfg_print_cstr(pctx, " | "); 3053 cfg_print_cstr(pctx, "*"); 3054 n++; 3055 POST(n); 3056 } 3057 cfg_print_cstr(pctx, " ) "); 3058 if ((*flagp & CFG_ADDR_WILDOK) != 0) { 3059 cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]"); 3060 } else { 3061 cfg_print_cstr(pctx, "[ port <integer> ]"); 3062 } 3063 if ((*flagp & CFG_ADDR_DSCPOK) != 0) { 3064 cfg_print_cstr(pctx, " [ dscp <integer> ]"); 3065 } 3066 } 3067 3068 bool 3069 cfg_obj_issockaddr(const cfg_obj_t *obj) { 3070 REQUIRE(obj != NULL); 3071 return (obj->type->rep == &cfg_rep_sockaddr); 3072 } 3073 3074 const isc_sockaddr_t * 3075 cfg_obj_assockaddr(const cfg_obj_t *obj) { 3076 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr); 3077 return (&obj->value.sockaddr); 3078 } 3079 3080 isc_dscp_t 3081 cfg_obj_getdscp(const cfg_obj_t *obj) { 3082 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr); 3083 return (obj->value.sockaddrdscp.dscp); 3084 } 3085 3086 isc_result_t 3087 cfg_gettoken(cfg_parser_t *pctx, int options) { 3088 isc_result_t result; 3089 3090 REQUIRE(pctx != NULL); 3091 3092 if (pctx->seen_eof) 3093 return (ISC_R_SUCCESS); 3094 3095 options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE); 3096 3097 redo: 3098 pctx->token.type = isc_tokentype_unknown; 3099 result = isc_lex_gettoken(pctx->lexer, options, &pctx->token); 3100 pctx->ungotten = false; 3101 pctx->line = isc_lex_getsourceline(pctx->lexer); 3102 3103 switch (result) { 3104 case ISC_R_SUCCESS: 3105 if (pctx->token.type == isc_tokentype_eof) { 3106 result = isc_lex_close(pctx->lexer); 3107 INSIST(result == ISC_R_NOMORE || 3108 result == ISC_R_SUCCESS); 3109 3110 if (isc_lex_getsourcename(pctx->lexer) != NULL) { 3111 /* 3112 * Closed an included file, not the main file. 3113 */ 3114 cfg_listelt_t *elt; 3115 elt = ISC_LIST_TAIL(pctx->open_files-> 3116 value.list); 3117 INSIST(elt != NULL); 3118 ISC_LIST_UNLINK(pctx->open_files-> 3119 value.list, elt, link); 3120 ISC_LIST_APPEND(pctx->closed_files-> 3121 value.list, elt, link); 3122 goto redo; 3123 } 3124 pctx->seen_eof = true; 3125 } 3126 break; 3127 3128 case ISC_R_NOSPACE: 3129 /* More understandable than "ran out of space". */ 3130 cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big"); 3131 break; 3132 3133 case ISC_R_IOERROR: 3134 cfg_parser_error(pctx, 0, "%s", 3135 isc_result_totext(result)); 3136 break; 3137 3138 default: 3139 cfg_parser_error(pctx, CFG_LOG_NEAR, "%s", 3140 isc_result_totext(result)); 3141 break; 3142 } 3143 return (result); 3144 } 3145 3146 void 3147 cfg_ungettoken(cfg_parser_t *pctx) { 3148 REQUIRE(pctx != NULL); 3149 3150 if (pctx->seen_eof) 3151 return; 3152 isc_lex_ungettoken(pctx->lexer, &pctx->token); 3153 pctx->ungotten = true; 3154 } 3155 3156 isc_result_t 3157 cfg_peektoken(cfg_parser_t *pctx, int options) { 3158 isc_result_t result; 3159 3160 REQUIRE(pctx != NULL); 3161 3162 CHECK(cfg_gettoken(pctx, options)); 3163 cfg_ungettoken(pctx); 3164 cleanup: 3165 return (result); 3166 } 3167 3168 /* 3169 * Get a string token, accepting both the quoted and the unquoted form. 3170 * Log an error if the next token is not a string. 3171 */ 3172 static isc_result_t 3173 cfg_getstringtoken(cfg_parser_t *pctx) { 3174 isc_result_t result; 3175 3176 result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING); 3177 if (result != ISC_R_SUCCESS) 3178 return (result); 3179 3180 if (pctx->token.type != isc_tokentype_string && 3181 pctx->token.type != isc_tokentype_qstring) { 3182 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string"); 3183 return (ISC_R_UNEXPECTEDTOKEN); 3184 } 3185 return (ISC_R_SUCCESS); 3186 } 3187 3188 void 3189 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) { 3190 va_list args; 3191 3192 REQUIRE(pctx != NULL); 3193 REQUIRE(fmt != NULL); 3194 3195 va_start(args, fmt); 3196 parser_complain(pctx, false, flags, fmt, args); 3197 va_end(args); 3198 pctx->errors++; 3199 } 3200 3201 void 3202 cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) { 3203 va_list args; 3204 3205 REQUIRE(pctx != NULL); 3206 REQUIRE(fmt != NULL); 3207 3208 va_start(args, fmt); 3209 parser_complain(pctx, true, flags, fmt, args); 3210 va_end(args); 3211 pctx->warnings++; 3212 } 3213 3214 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */ 3215 3216 static bool 3217 have_current_file(cfg_parser_t *pctx) { 3218 cfg_listelt_t *elt; 3219 if (pctx->open_files == NULL) 3220 return (false); 3221 3222 elt = ISC_LIST_TAIL(pctx->open_files->value.list); 3223 if (elt == NULL) 3224 return (false); 3225 3226 return (true); 3227 } 3228 3229 static char * 3230 current_file(cfg_parser_t *pctx) { 3231 static char none[] = "none"; 3232 cfg_listelt_t *elt; 3233 cfg_obj_t *fileobj; 3234 3235 if (!have_current_file(pctx)) 3236 return (none); 3237 3238 elt = ISC_LIST_TAIL(pctx->open_files->value.list); 3239 if (elt == NULL) /* shouldn't be possible, but... */ 3240 return (none); 3241 3242 fileobj = elt->obj; 3243 INSIST(fileobj->type == &cfg_type_qstring); 3244 return (fileobj->value.string.base); 3245 } 3246 3247 static void 3248 parser_complain(cfg_parser_t *pctx, bool is_warning, 3249 unsigned int flags, const char *format, 3250 va_list args) 3251 { 3252 char tokenbuf[MAX_LOG_TOKEN + 10]; 3253 static char where[PATH_MAX + 100]; 3254 static char message[2048]; 3255 int level = ISC_LOG_ERROR; 3256 const char *prep = ""; 3257 size_t len; 3258 3259 if (is_warning) 3260 level = ISC_LOG_WARNING; 3261 3262 where[0] = '\0'; 3263 if (have_current_file(pctx)) 3264 snprintf(where, sizeof(where), "%s:%u: ", 3265 current_file(pctx), pctx->line); 3266 else if (pctx->buf_name != NULL) 3267 snprintf(where, sizeof(where), "%s: ", pctx->buf_name); 3268 3269 len = vsnprintf(message, sizeof(message), format, args); 3270 #define ELIPSIS " ... " 3271 if (len >= sizeof(message)) { 3272 message[sizeof(message) - sizeof(ELIPSIS)] = 0; 3273 strlcat(message, ELIPSIS, sizeof(message)); 3274 } 3275 3276 if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) { 3277 isc_region_t r; 3278 3279 if (pctx->ungotten) 3280 (void)cfg_gettoken(pctx, 0); 3281 3282 if (pctx->token.type == isc_tokentype_eof) { 3283 snprintf(tokenbuf, sizeof(tokenbuf), "end of file"); 3284 } else if (pctx->token.type == isc_tokentype_unknown) { 3285 flags = 0; 3286 tokenbuf[0] = '\0'; 3287 } else { 3288 isc_lex_getlasttokentext(pctx->lexer, 3289 &pctx->token, &r); 3290 if (r.length > MAX_LOG_TOKEN) 3291 snprintf(tokenbuf, sizeof(tokenbuf), 3292 "'%.*s...'", MAX_LOG_TOKEN, r.base); 3293 else 3294 snprintf(tokenbuf, sizeof(tokenbuf), 3295 "'%.*s'", (int)r.length, r.base); 3296 } 3297 3298 /* Choose a preposition. */ 3299 if ((flags & CFG_LOG_NEAR) != 0) { 3300 prep = " near "; 3301 } else if ((flags & CFG_LOG_BEFORE) != 0) { 3302 prep = " before "; 3303 } else { 3304 prep = " "; 3305 } 3306 } else { 3307 tokenbuf[0] = '\0'; 3308 } 3309 isc_log_write(pctx->lctx, CAT, MOD, level, 3310 "%s%s%s%s", where, message, prep, tokenbuf); 3311 } 3312 3313 void 3314 cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level, 3315 const char *fmt, ...) { 3316 va_list ap; 3317 char msgbuf[2048]; 3318 3319 REQUIRE(obj != NULL); 3320 REQUIRE(fmt != NULL); 3321 3322 if (! isc_log_wouldlog(lctx, level)) 3323 return; 3324 3325 va_start(ap, fmt); 3326 vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); 3327 va_end(ap); 3328 3329 if (obj->file != NULL) { 3330 isc_log_write(lctx, CAT, MOD, level, 3331 "%s:%u: %s", obj->file, obj->line, msgbuf); 3332 } else { 3333 isc_log_write(lctx, CAT, MOD, level, "%s", msgbuf); 3334 } 3335 } 3336 3337 const char * 3338 cfg_obj_file(const cfg_obj_t *obj) { 3339 REQUIRE(obj != NULL); 3340 3341 return (obj->file); 3342 } 3343 3344 unsigned int 3345 cfg_obj_line(const cfg_obj_t *obj) { 3346 REQUIRE(obj != NULL); 3347 3348 return (obj->line); 3349 } 3350 3351 isc_result_t 3352 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 3353 cfg_obj_t *obj; 3354 3355 REQUIRE(pctx != NULL); 3356 REQUIRE(type != NULL); 3357 REQUIRE(ret != NULL && *ret == NULL); 3358 3359 obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t)); 3360 if (obj == NULL) 3361 return (ISC_R_NOMEMORY); 3362 3363 obj->type = type; 3364 obj->file = current_file(pctx); 3365 obj->line = pctx->line; 3366 obj->pctx = pctx; 3367 3368 isc_refcount_init(&obj->references, 1); 3369 3370 *ret = obj; 3371 3372 return (ISC_R_SUCCESS); 3373 } 3374 3375 3376 static void 3377 map_symtabitem_destroy(char *key, unsigned int type, 3378 isc_symvalue_t symval, void *userarg) 3379 { 3380 cfg_obj_t *obj = symval.as_pointer; 3381 cfg_parser_t *pctx = (cfg_parser_t *)userarg; 3382 3383 UNUSED(key); 3384 UNUSED(type); 3385 3386 cfg_obj_destroy(pctx, &obj); 3387 } 3388 3389 static isc_result_t 3390 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 3391 isc_result_t result; 3392 isc_symtab_t *symtab = NULL; 3393 cfg_obj_t *obj = NULL; 3394 3395 CHECK(cfg_create_obj(pctx, type, &obj)); 3396 CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */ 3397 map_symtabitem_destroy, 3398 pctx, false, &symtab)); 3399 obj->value.map.symtab = symtab; 3400 obj->value.map.id = NULL; 3401 3402 *ret = obj; 3403 return (ISC_R_SUCCESS); 3404 3405 cleanup: 3406 if (obj != NULL) 3407 isc_mem_put(pctx->mctx, obj, sizeof(*obj)); 3408 return (result); 3409 } 3410 3411 static void 3412 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) { 3413 CLEANUP_OBJ(obj->value.map.id); 3414 isc_symtab_destroy(&obj->value.map.symtab); 3415 } 3416 3417 bool 3418 cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) { 3419 3420 REQUIRE(obj != NULL); 3421 REQUIRE(type != NULL); 3422 3423 return (obj->type == type); 3424 } 3425 3426 /* 3427 * Destroy 'obj', a configuration object created in 'pctx'. 3428 */ 3429 void 3430 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) { 3431 REQUIRE(objp != NULL && *objp != NULL); 3432 REQUIRE(pctx != NULL); 3433 3434 cfg_obj_t *obj = *objp; 3435 *objp = NULL; 3436 3437 if (isc_refcount_decrement(&obj->references) == 1) { 3438 obj->type->rep->free(pctx, obj); 3439 isc_refcount_destroy(&obj->references); 3440 isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t)); 3441 } 3442 } 3443 3444 void 3445 cfg_obj_attach(cfg_obj_t *src, cfg_obj_t **dest) { 3446 REQUIRE(src != NULL); 3447 REQUIRE(dest != NULL && *dest == NULL); 3448 3449 isc_refcount_increment(&src->references); 3450 *dest = src; 3451 } 3452 3453 static void 3454 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) { 3455 UNUSED(pctx); 3456 UNUSED(obj); 3457 } 3458 3459 void 3460 cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) { 3461 REQUIRE(pctx != NULL); 3462 REQUIRE(type != NULL); 3463 3464 type->doc(pctx, type); 3465 } 3466 3467 void 3468 cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) { 3469 REQUIRE(pctx != NULL); 3470 REQUIRE(type != NULL); 3471 3472 cfg_print_cstr(pctx, "<"); 3473 cfg_print_cstr(pctx, type->name); 3474 cfg_print_cstr(pctx, ">"); 3475 } 3476 3477 void 3478 cfg_print_grammar(const cfg_type_t *type, 3479 void (*f)(void *closure, const char *text, int textlen), 3480 void *closure) 3481 { 3482 cfg_printer_t pctx; 3483 3484 pctx.f = f; 3485 pctx.closure = closure; 3486 pctx.indent = 0; 3487 pctx.flags = 0; 3488 cfg_doc_obj(&pctx, type); 3489 } 3490 3491 isc_result_t 3492 cfg_parser_mapadd(cfg_parser_t *pctx, cfg_obj_t *mapobj, 3493 cfg_obj_t *obj, const char *clausename) 3494 { 3495 isc_result_t result = ISC_R_SUCCESS; 3496 const cfg_map_t *map; 3497 isc_symvalue_t symval; 3498 cfg_obj_t *destobj = NULL; 3499 cfg_listelt_t *elt = NULL; 3500 const cfg_clausedef_t * const *clauseset; 3501 const cfg_clausedef_t *clause; 3502 3503 REQUIRE(pctx != NULL); 3504 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 3505 REQUIRE(obj != NULL); 3506 REQUIRE(clausename != NULL); 3507 3508 map = &mapobj->value.map; 3509 3510 clause = NULL; 3511 for (clauseset = map->clausesets; *clauseset != NULL; clauseset++) { 3512 for (clause = *clauseset; clause->name != NULL; clause++) { 3513 if (strcasecmp(clause->name, clausename) == 0) { 3514 goto breakout; 3515 } 3516 } 3517 } 3518 3519 breakout: 3520 if (clause == NULL || clause->name == NULL) 3521 return (ISC_R_FAILURE); 3522 3523 result = isc_symtab_lookup(map->symtab, clausename, 0, &symval); 3524 if (result == ISC_R_NOTFOUND) { 3525 if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) { 3526 CHECK(cfg_create_list(pctx, &cfg_type_implicitlist, 3527 &destobj)); 3528 CHECK(create_listelt(pctx, &elt)); 3529 cfg_obj_attach(obj, &elt->obj); 3530 ISC_LIST_APPEND(destobj->value.list, elt, link); 3531 symval.as_pointer = destobj; 3532 } else 3533 symval.as_pointer = obj; 3534 3535 CHECK(isc_symtab_define(map->symtab, clausename, 1, symval, 3536 isc_symexists_reject)); 3537 } else { 3538 cfg_obj_t *destobj2 = symval.as_pointer; 3539 3540 INSIST(result == ISC_R_SUCCESS); 3541 3542 if (destobj2->type == &cfg_type_implicitlist) { 3543 CHECK(create_listelt(pctx, &elt)); 3544 cfg_obj_attach(obj, &elt->obj); 3545 ISC_LIST_APPEND(destobj2->value.list, elt, link); 3546 } else 3547 result = ISC_R_EXISTS; 3548 } 3549 3550 destobj = NULL; 3551 elt = NULL; 3552 3553 cleanup: 3554 if (elt != NULL) 3555 free_listelt(pctx, elt); 3556 CLEANUP_OBJ(destobj); 3557 3558 return (result); 3559 } 3560 3561 isc_result_t 3562 cfg_pluginlist_foreach(const cfg_obj_t *config, const cfg_obj_t *list, 3563 isc_log_t *lctx, pluginlist_cb_t *callback, 3564 void *callback_data) 3565 { 3566 isc_result_t result = ISC_R_SUCCESS; 3567 const cfg_listelt_t *element; 3568 3569 REQUIRE(config != NULL); 3570 REQUIRE(callback != NULL); 3571 3572 for (element = cfg_list_first(list); 3573 element != NULL; 3574 element = cfg_list_next(element)) 3575 { 3576 const cfg_obj_t *plugin = cfg_listelt_value(element); 3577 const cfg_obj_t *obj; 3578 const char *type, *library; 3579 const char *parameters = NULL; 3580 3581 /* Get the path to the plugin module. */ 3582 obj = cfg_tuple_get(plugin, "type"); 3583 type = cfg_obj_asstring(obj); 3584 3585 /* Only query plugins are supported currently. */ 3586 if (strcasecmp(type, "query") != 0) { 3587 cfg_obj_log(obj, lctx, ISC_LOG_ERROR, 3588 "unsupported plugin type"); 3589 return (ISC_R_FAILURE); 3590 } 3591 3592 library = cfg_obj_asstring(cfg_tuple_get(plugin, "library")); 3593 3594 obj = cfg_tuple_get(plugin, "parameters"); 3595 if (obj != NULL && cfg_obj_isstring(obj)) { 3596 parameters = cfg_obj_asstring(obj); 3597 } 3598 3599 result = callback(config, obj, library, parameters, 3600 callback_data); 3601 if (result != ISC_R_SUCCESS) { 3602 break; 3603 } 3604 } 3605 3606 return (result); 3607 } 3608