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