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