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