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