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