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