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