1135446Strhodes/* 2262706Serwin * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 2000-2003 Internet Software Consortium. 4135446Strhodes * 5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18234010Sdougb/* $Id$ */ 19135446Strhodes 20170222Sdougb/*! \file */ 21170222Sdougb 22135446Strhodes#include <config.h> 23135446Strhodes 24135446Strhodes#include <isc/buffer.h> 25135446Strhodes#include <isc/dir.h> 26135446Strhodes#include <isc/formatcheck.h> 27135446Strhodes#include <isc/lex.h> 28135446Strhodes#include <isc/log.h> 29135446Strhodes#include <isc/mem.h> 30135446Strhodes#include <isc/net.h> 31135446Strhodes#include <isc/netaddr.h> 32224092Sdougb#include <isc/netscope.h> 33135446Strhodes#include <isc/print.h> 34135446Strhodes#include <isc/string.h> 35135446Strhodes#include <isc/sockaddr.h> 36224092Sdougb#include <isc/symtab.h> 37135446Strhodes#include <isc/util.h> 38135446Strhodes 39135446Strhodes#include <isccfg/cfg.h> 40135446Strhodes#include <isccfg/grammar.h> 41135446Strhodes#include <isccfg/log.h> 42135446Strhodes 43135446Strhodes/* Shorthand */ 44135446Strhodes#define CAT CFG_LOGCATEGORY_CONFIG 45135446Strhodes#define MOD CFG_LOGMODULE_PARSER 46135446Strhodes 47135446Strhodes#define MAP_SYM 1 /* Unique type for isc_symtab */ 48135446Strhodes 49135446Strhodes#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base) 50135446Strhodes 51135446Strhodes/* Check a return value. */ 52135446Strhodes#define CHECK(op) \ 53193149Sdougb do { result = (op); \ 54135446Strhodes if (result != ISC_R_SUCCESS) goto cleanup; \ 55135446Strhodes } while (0) 56135446Strhodes 57135446Strhodes/* Clean up a configuration object if non-NULL. */ 58135446Strhodes#define CLEANUP_OBJ(obj) \ 59135446Strhodes do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0) 60135446Strhodes 61135446Strhodes 62135446Strhodes/* 63135446Strhodes * Forward declarations of static functions. 64135446Strhodes */ 65135446Strhodes 66135446Strhodesstatic void 67135446Strhodesfree_tuple(cfg_parser_t *pctx, cfg_obj_t *obj); 68135446Strhodes 69135446Strhodesstatic isc_result_t 70135446Strhodesparse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); 71135446Strhodes 72135446Strhodesstatic void 73165071Sdougbprint_list(cfg_printer_t *pctx, const cfg_obj_t *obj); 74135446Strhodes 75135446Strhodesstatic void 76135446Strhodesfree_list(cfg_parser_t *pctx, cfg_obj_t *obj); 77135446Strhodes 78135446Strhodesstatic isc_result_t 79135446Strhodescreate_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp); 80135446Strhodes 81135446Strhodesstatic isc_result_t 82135446Strhodescreate_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type, 83135446Strhodes cfg_obj_t **ret); 84135446Strhodes 85135446Strhodesstatic void 86135446Strhodesfree_string(cfg_parser_t *pctx, cfg_obj_t *obj); 87135446Strhodes 88135446Strhodesstatic isc_result_t 89135446Strhodescreate_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp); 90135446Strhodes 91135446Strhodesstatic void 92135446Strhodesfree_map(cfg_parser_t *pctx, cfg_obj_t *obj); 93135446Strhodes 94135446Strhodesstatic isc_result_t 95135446Strhodesparse_symtab_elt(cfg_parser_t *pctx, const char *name, 96135446Strhodes cfg_type_t *elttype, isc_symtab_t *symtab, 97135446Strhodes isc_boolean_t callback); 98135446Strhodes 99135446Strhodesstatic void 100135446Strhodesfree_noop(cfg_parser_t *pctx, cfg_obj_t *obj); 101135446Strhodes 102135446Strhodesstatic isc_result_t 103135446Strhodescfg_getstringtoken(cfg_parser_t *pctx); 104135446Strhodes 105135446Strhodesstatic void 106135446Strhodesparser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning, 107135446Strhodes unsigned int flags, const char *format, va_list args); 108135446Strhodes 109135446Strhodes/* 110135446Strhodes * Data representations. These correspond to members of the 111135446Strhodes * "value" union in struct cfg_obj (except "void", which does 112135446Strhodes * not need a union member). 113135446Strhodes */ 114135446Strhodes 115135446Strhodescfg_rep_t cfg_rep_uint32 = { "uint32", free_noop }; 116135446Strhodescfg_rep_t cfg_rep_uint64 = { "uint64", free_noop }; 117135446Strhodescfg_rep_t cfg_rep_string = { "string", free_string }; 118135446Strhodescfg_rep_t cfg_rep_boolean = { "boolean", free_noop }; 119135446Strhodescfg_rep_t cfg_rep_map = { "map", free_map }; 120135446Strhodescfg_rep_t cfg_rep_list = { "list", free_list }; 121135446Strhodescfg_rep_t cfg_rep_tuple = { "tuple", free_tuple }; 122135446Strhodescfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop }; 123135446Strhodescfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop }; 124135446Strhodescfg_rep_t cfg_rep_void = { "void", free_noop }; 125135446Strhodes 126135446Strhodes/* 127135446Strhodes * Configuration type definitions. 128135446Strhodes */ 129135446Strhodes 130170222Sdougb/*% 131135446Strhodes * An implicit list. These are formed by clauses that occur multiple times. 132135446Strhodes */ 133135446Strhodesstatic cfg_type_t cfg_type_implicitlist = { 134135446Strhodes "implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL }; 135135446Strhodes 136135446Strhodes/* Functions. */ 137135446Strhodes 138135446Strhodesvoid 139165071Sdougbcfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) { 140135446Strhodes obj->type->print(pctx, obj); 141135446Strhodes} 142135446Strhodes 143135446Strhodesvoid 144135446Strhodescfg_print_chars(cfg_printer_t *pctx, const char *text, int len) { 145135446Strhodes pctx->f(pctx->closure, text, len); 146135446Strhodes} 147135446Strhodes 148135446Strhodesstatic void 149135446Strhodesprint_open(cfg_printer_t *pctx) { 150135446Strhodes cfg_print_chars(pctx, "{\n", 2); 151135446Strhodes pctx->indent++; 152135446Strhodes} 153135446Strhodes 154135446Strhodesstatic void 155135446Strhodesprint_indent(cfg_printer_t *pctx) { 156135446Strhodes int indent = pctx->indent; 157135446Strhodes while (indent > 0) { 158135446Strhodes cfg_print_chars(pctx, "\t", 1); 159135446Strhodes indent--; 160135446Strhodes } 161135446Strhodes} 162135446Strhodes 163135446Strhodesstatic void 164135446Strhodesprint_close(cfg_printer_t *pctx) { 165135446Strhodes pctx->indent--; 166135446Strhodes print_indent(pctx); 167135446Strhodes cfg_print_chars(pctx, "}", 1); 168135446Strhodes} 169135446Strhodes 170135446Strhodesisc_result_t 171135446Strhodescfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 172135446Strhodes isc_result_t result; 173135446Strhodes INSIST(ret != NULL && *ret == NULL); 174135446Strhodes result = type->parse(pctx, type, ret); 175135446Strhodes if (result != ISC_R_SUCCESS) 176135446Strhodes return (result); 177135446Strhodes INSIST(*ret != NULL); 178135446Strhodes return (ISC_R_SUCCESS); 179135446Strhodes} 180135446Strhodes 181135446Strhodesvoid 182165071Sdougbcfg_print(const cfg_obj_t *obj, 183135446Strhodes void (*f)(void *closure, const char *text, int textlen), 184135446Strhodes void *closure) 185135446Strhodes{ 186262706Serwin cfg_printx(obj, 0, f, closure); 187262706Serwin} 188262706Serwin 189262706Serwinvoid 190262706Serwincfg_printx(const cfg_obj_t *obj, unsigned int flags, 191262706Serwin void (*f)(void *closure, const char *text, int textlen), 192262706Serwin void *closure) 193262706Serwin{ 194135446Strhodes cfg_printer_t pctx; 195135446Strhodes pctx.f = f; 196135446Strhodes pctx.closure = closure; 197135446Strhodes pctx.indent = 0; 198262706Serwin pctx.flags = flags; 199135446Strhodes obj->type->print(&pctx, obj); 200135446Strhodes} 201135446Strhodes 202135446Strhodes/* Tuples. */ 203193149Sdougb 204135446Strhodesisc_result_t 205135446Strhodescfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 206135446Strhodes isc_result_t result; 207135446Strhodes const cfg_tuplefielddef_t *fields = type->of; 208135446Strhodes const cfg_tuplefielddef_t *f; 209135446Strhodes cfg_obj_t *obj = NULL; 210135446Strhodes unsigned int nfields = 0; 211135446Strhodes int i; 212135446Strhodes 213135446Strhodes for (f = fields; f->name != NULL; f++) 214135446Strhodes nfields++; 215135446Strhodes 216135446Strhodes CHECK(cfg_create_obj(pctx, type, &obj)); 217135446Strhodes obj->value.tuple = isc_mem_get(pctx->mctx, 218135446Strhodes nfields * sizeof(cfg_obj_t *)); 219135446Strhodes if (obj->value.tuple == NULL) { 220135446Strhodes result = ISC_R_NOMEMORY; 221135446Strhodes goto cleanup; 222135446Strhodes } 223135446Strhodes for (f = fields, i = 0; f->name != NULL; f++, i++) 224135446Strhodes obj->value.tuple[i] = NULL; 225135446Strhodes *ret = obj; 226135446Strhodes return (ISC_R_SUCCESS); 227135446Strhodes 228135446Strhodes cleanup: 229135446Strhodes if (obj != NULL) 230135446Strhodes isc_mem_put(pctx->mctx, obj, sizeof(*obj)); 231135446Strhodes return (result); 232135446Strhodes} 233135446Strhodes 234135446Strhodesisc_result_t 235135446Strhodescfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) 236135446Strhodes{ 237135446Strhodes isc_result_t result; 238135446Strhodes const cfg_tuplefielddef_t *fields = type->of; 239135446Strhodes const cfg_tuplefielddef_t *f; 240135446Strhodes cfg_obj_t *obj = NULL; 241135446Strhodes unsigned int i; 242135446Strhodes 243135446Strhodes CHECK(cfg_create_tuple(pctx, type, &obj)); 244135446Strhodes for (f = fields, i = 0; f->name != NULL; f++, i++) 245135446Strhodes CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i])); 246135446Strhodes 247135446Strhodes *ret = obj; 248135446Strhodes return (ISC_R_SUCCESS); 249135446Strhodes 250135446Strhodes cleanup: 251135446Strhodes CLEANUP_OBJ(obj); 252135446Strhodes return (result); 253135446Strhodes} 254135446Strhodes 255135446Strhodesvoid 256165071Sdougbcfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) { 257135446Strhodes unsigned int i; 258135446Strhodes const cfg_tuplefielddef_t *fields = obj->type->of; 259135446Strhodes const cfg_tuplefielddef_t *f; 260135446Strhodes isc_boolean_t need_space = ISC_FALSE; 261135446Strhodes 262135446Strhodes for (f = fields, i = 0; f->name != NULL; f++, i++) { 263165071Sdougb const cfg_obj_t *fieldobj = obj->value.tuple[i]; 264135446Strhodes if (need_space) 265135446Strhodes cfg_print_chars(pctx, " ", 1); 266135446Strhodes cfg_print_obj(pctx, fieldobj); 267135446Strhodes need_space = ISC_TF(fieldobj->type->print != cfg_print_void); 268135446Strhodes } 269135446Strhodes} 270135446Strhodes 271135446Strhodesvoid 272135446Strhodescfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) { 273135446Strhodes const cfg_tuplefielddef_t *fields = type->of; 274135446Strhodes const cfg_tuplefielddef_t *f; 275135446Strhodes isc_boolean_t need_space = ISC_FALSE; 276135446Strhodes 277135446Strhodes for (f = fields; f->name != NULL; f++) { 278135446Strhodes if (need_space) 279135446Strhodes cfg_print_chars(pctx, " ", 1); 280135446Strhodes cfg_doc_obj(pctx, f->type); 281135446Strhodes need_space = ISC_TF(f->type->print != cfg_print_void); 282135446Strhodes } 283135446Strhodes} 284135446Strhodes 285135446Strhodesstatic void 286135446Strhodesfree_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) { 287135446Strhodes unsigned int i; 288135446Strhodes const cfg_tuplefielddef_t *fields = obj->type->of; 289135446Strhodes const cfg_tuplefielddef_t *f; 290135446Strhodes unsigned int nfields = 0; 291135446Strhodes 292135446Strhodes if (obj->value.tuple == NULL) 293135446Strhodes return; 294135446Strhodes 295135446Strhodes for (f = fields, i = 0; f->name != NULL; f++, i++) { 296135446Strhodes CLEANUP_OBJ(obj->value.tuple[i]); 297135446Strhodes nfields++; 298135446Strhodes } 299135446Strhodes isc_mem_put(pctx->mctx, obj->value.tuple, 300135446Strhodes nfields * sizeof(cfg_obj_t *)); 301135446Strhodes} 302135446Strhodes 303135446Strhodesisc_boolean_t 304165071Sdougbcfg_obj_istuple(const cfg_obj_t *obj) { 305135446Strhodes REQUIRE(obj != NULL); 306135446Strhodes return (ISC_TF(obj->type->rep == &cfg_rep_tuple)); 307135446Strhodes} 308135446Strhodes 309165071Sdougbconst cfg_obj_t * 310165071Sdougbcfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) { 311135446Strhodes unsigned int i; 312135446Strhodes const cfg_tuplefielddef_t *fields; 313135446Strhodes const cfg_tuplefielddef_t *f; 314193149Sdougb 315135446Strhodes REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple); 316135446Strhodes 317135446Strhodes fields = tupleobj->type->of; 318135446Strhodes for (f = fields, i = 0; f->name != NULL; f++, i++) { 319135446Strhodes if (strcmp(f->name, name) == 0) 320135446Strhodes return (tupleobj->value.tuple[i]); 321135446Strhodes } 322135446Strhodes INSIST(0); 323135446Strhodes return (NULL); 324135446Strhodes} 325135446Strhodes 326135446Strhodesisc_result_t 327135446Strhodescfg_parse_special(cfg_parser_t *pctx, int special) { 328193149Sdougb isc_result_t result; 329135446Strhodes CHECK(cfg_gettoken(pctx, 0)); 330135446Strhodes if (pctx->token.type == isc_tokentype_special && 331135446Strhodes pctx->token.value.as_char == special) 332135446Strhodes return (ISC_R_SUCCESS); 333135446Strhodes 334135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special); 335135446Strhodes return (ISC_R_UNEXPECTEDTOKEN); 336135446Strhodes cleanup: 337135446Strhodes return (result); 338135446Strhodes} 339135446Strhodes 340135446Strhodes/* 341135446Strhodes * Parse a required semicolon. If it is not there, log 342135446Strhodes * an error and increment the error count but continue 343135446Strhodes * parsing. Since the next token is pushed back, 344135446Strhodes * care must be taken to make sure it is eventually 345135446Strhodes * consumed or an infinite loop may result. 346135446Strhodes */ 347135446Strhodesstatic isc_result_t 348135446Strhodesparse_semicolon(cfg_parser_t *pctx) { 349193149Sdougb isc_result_t result; 350135446Strhodes CHECK(cfg_gettoken(pctx, 0)); 351135446Strhodes if (pctx->token.type == isc_tokentype_special && 352135446Strhodes pctx->token.value.as_char == ';') 353135446Strhodes return (ISC_R_SUCCESS); 354135446Strhodes 355135446Strhodes cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'"); 356135446Strhodes cfg_ungettoken(pctx); 357135446Strhodes cleanup: 358135446Strhodes return (result); 359135446Strhodes} 360135446Strhodes 361135446Strhodes/* 362135446Strhodes * Parse EOF, logging and returning an error if not there. 363135446Strhodes */ 364135446Strhodesstatic isc_result_t 365135446Strhodesparse_eof(cfg_parser_t *pctx) { 366193149Sdougb isc_result_t result; 367135446Strhodes CHECK(cfg_gettoken(pctx, 0)); 368135446Strhodes 369135446Strhodes if (pctx->token.type == isc_tokentype_eof) 370135446Strhodes return (ISC_R_SUCCESS); 371135446Strhodes 372135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error"); 373135446Strhodes return (ISC_R_UNEXPECTEDTOKEN); 374135446Strhodes cleanup: 375135446Strhodes return (result); 376135446Strhodes} 377135446Strhodes 378135446Strhodes/* A list of files, used internally for pctx->files. */ 379135446Strhodes 380135446Strhodesstatic cfg_type_t cfg_type_filelist = { 381135446Strhodes "filelist", NULL, print_list, NULL, &cfg_rep_list, 382135446Strhodes &cfg_type_qstring 383135446Strhodes}; 384135446Strhodes 385135446Strhodesisc_result_t 386135446Strhodescfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) { 387135446Strhodes isc_result_t result; 388135446Strhodes cfg_parser_t *pctx; 389135446Strhodes isc_lexspecials_t specials; 390135446Strhodes 391135446Strhodes REQUIRE(mctx != NULL); 392135446Strhodes REQUIRE(ret != NULL && *ret == NULL); 393135446Strhodes 394135446Strhodes pctx = isc_mem_get(mctx, sizeof(*pctx)); 395135446Strhodes if (pctx == NULL) 396135446Strhodes return (ISC_R_NOMEMORY); 397135446Strhodes 398254402Serwin pctx->mctx = NULL; 399254402Serwin isc_mem_attach(mctx, &pctx->mctx); 400254402Serwin 401224092Sdougb result = isc_refcount_init(&pctx->references, 1); 402224092Sdougb if (result != ISC_R_SUCCESS) { 403254402Serwin isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx)); 404224092Sdougb return (result); 405224092Sdougb } 406224092Sdougb 407135446Strhodes pctx->lctx = lctx; 408135446Strhodes pctx->lexer = NULL; 409135446Strhodes pctx->seen_eof = ISC_FALSE; 410135446Strhodes pctx->ungotten = ISC_FALSE; 411135446Strhodes pctx->errors = 0; 412135446Strhodes pctx->warnings = 0; 413135446Strhodes pctx->open_files = NULL; 414135446Strhodes pctx->closed_files = NULL; 415135446Strhodes pctx->line = 0; 416135446Strhodes pctx->callback = NULL; 417135446Strhodes pctx->callbackarg = NULL; 418135446Strhodes pctx->token.type = isc_tokentype_unknown; 419224092Sdougb pctx->flags = 0; 420135446Strhodes 421135446Strhodes memset(specials, 0, sizeof(specials)); 422135446Strhodes specials['{'] = 1; 423135446Strhodes specials['}'] = 1; 424135446Strhodes specials[';'] = 1; 425135446Strhodes specials['/'] = 1; 426135446Strhodes specials['"'] = 1; 427135446Strhodes specials['!'] = 1; 428135446Strhodes 429135446Strhodes CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer)); 430135446Strhodes 431135446Strhodes isc_lex_setspecials(pctx->lexer, specials); 432135446Strhodes isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C | 433135446Strhodes ISC_LEXCOMMENT_CPLUSPLUS | 434135446Strhodes ISC_LEXCOMMENT_SHELL)); 435135446Strhodes 436135446Strhodes CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files)); 437135446Strhodes CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files)); 438135446Strhodes 439135446Strhodes *ret = pctx; 440135446Strhodes return (ISC_R_SUCCESS); 441135446Strhodes 442135446Strhodes cleanup: 443135446Strhodes if (pctx->lexer != NULL) 444135446Strhodes isc_lex_destroy(&pctx->lexer); 445135446Strhodes CLEANUP_OBJ(pctx->open_files); 446135446Strhodes CLEANUP_OBJ(pctx->closed_files); 447254402Serwin isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx)); 448135446Strhodes return (result); 449135446Strhodes} 450135446Strhodes 451135446Strhodesstatic isc_result_t 452135446Strhodesparser_openfile(cfg_parser_t *pctx, const char *filename) { 453135446Strhodes isc_result_t result; 454135446Strhodes cfg_listelt_t *elt = NULL; 455135446Strhodes cfg_obj_t *stringobj = NULL; 456135446Strhodes 457135446Strhodes result = isc_lex_openfile(pctx->lexer, filename); 458135446Strhodes if (result != ISC_R_SUCCESS) { 459135446Strhodes cfg_parser_error(pctx, 0, "open: %s: %s", 460135446Strhodes filename, isc_result_totext(result)); 461135446Strhodes goto cleanup; 462135446Strhodes } 463135446Strhodes 464135446Strhodes CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj)); 465135446Strhodes CHECK(create_listelt(pctx, &elt)); 466135446Strhodes elt->obj = stringobj; 467135446Strhodes ISC_LIST_APPEND(pctx->open_files->value.list, elt, link); 468135446Strhodes 469135446Strhodes return (ISC_R_SUCCESS); 470135446Strhodes cleanup: 471135446Strhodes CLEANUP_OBJ(stringobj); 472135446Strhodes return (result); 473135446Strhodes} 474135446Strhodes 475135446Strhodesvoid 476135446Strhodescfg_parser_setcallback(cfg_parser_t *pctx, 477135446Strhodes cfg_parsecallback_t callback, 478135446Strhodes void *arg) 479135446Strhodes{ 480135446Strhodes pctx->callback = callback; 481135446Strhodes pctx->callbackarg = arg; 482135446Strhodes} 483135446Strhodes 484135446Strhodes/* 485135446Strhodes * Parse a configuration using a pctx where a lexer has already 486135446Strhodes * been set up with a source. 487135446Strhodes */ 488135446Strhodesstatic isc_result_t 489135446Strhodesparse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 490135446Strhodes isc_result_t result; 491135446Strhodes cfg_obj_t *obj = NULL; 492135446Strhodes 493135446Strhodes result = cfg_parse_obj(pctx, type, &obj); 494135446Strhodes 495135446Strhodes if (pctx->errors != 0) { 496135446Strhodes /* Errors have been logged. */ 497135446Strhodes if (result == ISC_R_SUCCESS) 498135446Strhodes result = ISC_R_FAILURE; 499135446Strhodes goto cleanup; 500135446Strhodes } 501135446Strhodes 502135446Strhodes if (result != ISC_R_SUCCESS) { 503135446Strhodes /* Parsing failed but no errors have been logged. */ 504135446Strhodes cfg_parser_error(pctx, 0, "parsing failed"); 505135446Strhodes goto cleanup; 506135446Strhodes } 507135446Strhodes 508135446Strhodes CHECK(parse_eof(pctx)); 509135446Strhodes 510135446Strhodes *ret = obj; 511135446Strhodes return (ISC_R_SUCCESS); 512135446Strhodes 513135446Strhodes cleanup: 514135446Strhodes CLEANUP_OBJ(obj); 515135446Strhodes return (result); 516135446Strhodes} 517135446Strhodes 518135446Strhodesisc_result_t 519135446Strhodescfg_parse_file(cfg_parser_t *pctx, const char *filename, 520135446Strhodes const cfg_type_t *type, cfg_obj_t **ret) 521135446Strhodes{ 522135446Strhodes isc_result_t result; 523135446Strhodes 524135446Strhodes REQUIRE(filename != NULL); 525135446Strhodes 526135446Strhodes CHECK(parser_openfile(pctx, filename)); 527135446Strhodes CHECK(parse2(pctx, type, ret)); 528135446Strhodes cleanup: 529135446Strhodes return (result); 530135446Strhodes} 531135446Strhodes 532135446Strhodes 533135446Strhodesisc_result_t 534135446Strhodescfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer, 535135446Strhodes const cfg_type_t *type, cfg_obj_t **ret) 536135446Strhodes{ 537135446Strhodes isc_result_t result; 538135446Strhodes REQUIRE(buffer != NULL); 539193149Sdougb CHECK(isc_lex_openbuffer(pctx->lexer, buffer)); 540135446Strhodes CHECK(parse2(pctx, type, ret)); 541135446Strhodes cleanup: 542135446Strhodes return (result); 543135446Strhodes} 544135446Strhodes 545135446Strhodesvoid 546224092Sdougbcfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) { 547224092Sdougb REQUIRE(src != NULL); 548224092Sdougb REQUIRE(dest != NULL && *dest == NULL); 549224092Sdougb isc_refcount_increment(&src->references, NULL); 550224092Sdougb *dest = src; 551224092Sdougb} 552224092Sdougb 553224092Sdougbvoid 554135446Strhodescfg_parser_destroy(cfg_parser_t **pctxp) { 555135446Strhodes cfg_parser_t *pctx = *pctxp; 556224092Sdougb unsigned int refs; 557224092Sdougb 558224092Sdougb isc_refcount_decrement(&pctx->references, &refs); 559224092Sdougb if (refs == 0) { 560224092Sdougb isc_lex_destroy(&pctx->lexer); 561224092Sdougb /* 562224092Sdougb * Cleaning up open_files does not 563224092Sdougb * close the files; that was already done 564224092Sdougb * by closing the lexer. 565224092Sdougb */ 566224092Sdougb CLEANUP_OBJ(pctx->open_files); 567224092Sdougb CLEANUP_OBJ(pctx->closed_files); 568254402Serwin isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx)); 569224092Sdougb } 570135446Strhodes *pctxp = NULL; 571135446Strhodes} 572135446Strhodes 573135446Strhodes/* 574135446Strhodes * void 575135446Strhodes */ 576135446Strhodesisc_result_t 577135446Strhodescfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 578135446Strhodes UNUSED(type); 579135446Strhodes return (cfg_create_obj(pctx, &cfg_type_void, ret)); 580135446Strhodes} 581135446Strhodes 582135446Strhodesvoid 583165071Sdougbcfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) { 584135446Strhodes UNUSED(pctx); 585135446Strhodes UNUSED(obj); 586135446Strhodes} 587135446Strhodes 588135446Strhodesvoid 589135446Strhodescfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) { 590135446Strhodes UNUSED(pctx); 591135446Strhodes UNUSED(type); 592135446Strhodes} 593135446Strhodes 594135446Strhodesisc_boolean_t 595165071Sdougbcfg_obj_isvoid(const cfg_obj_t *obj) { 596135446Strhodes REQUIRE(obj != NULL); 597135446Strhodes return (ISC_TF(obj->type->rep == &cfg_rep_void)); 598135446Strhodes} 599135446Strhodes 600135446Strhodescfg_type_t cfg_type_void = { 601135446Strhodes "void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void, 602135446Strhodes NULL }; 603135446Strhodes 604135446Strhodes 605135446Strhodes/* 606135446Strhodes * uint32 607135446Strhodes */ 608135446Strhodesisc_result_t 609135446Strhodescfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 610193149Sdougb isc_result_t result; 611135446Strhodes cfg_obj_t *obj = NULL; 612135446Strhodes UNUSED(type); 613135446Strhodes 614135446Strhodes CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER)); 615135446Strhodes if (pctx->token.type != isc_tokentype_number) { 616135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number"); 617135446Strhodes return (ISC_R_UNEXPECTEDTOKEN); 618135446Strhodes } 619135446Strhodes 620135446Strhodes CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj)); 621135446Strhodes 622135446Strhodes obj->value.uint32 = pctx->token.value.as_ulong; 623135446Strhodes *ret = obj; 624135446Strhodes cleanup: 625135446Strhodes return (result); 626135446Strhodes} 627135446Strhodes 628135446Strhodesvoid 629135446Strhodescfg_print_cstr(cfg_printer_t *pctx, const char *s) { 630135446Strhodes cfg_print_chars(pctx, s, strlen(s)); 631135446Strhodes} 632135446Strhodes 633135446Strhodesvoid 634135446Strhodescfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) { 635135446Strhodes char buf[32]; 636135446Strhodes snprintf(buf, sizeof(buf), "%u", u); 637135446Strhodes cfg_print_cstr(pctx, buf); 638135446Strhodes} 639135446Strhodes 640135446Strhodesvoid 641165071Sdougbcfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) { 642135446Strhodes cfg_print_rawuint(pctx, obj->value.uint32); 643135446Strhodes} 644135446Strhodes 645135446Strhodesisc_boolean_t 646165071Sdougbcfg_obj_isuint32(const cfg_obj_t *obj) { 647135446Strhodes REQUIRE(obj != NULL); 648135446Strhodes return (ISC_TF(obj->type->rep == &cfg_rep_uint32)); 649135446Strhodes} 650135446Strhodes 651135446Strhodesisc_uint32_t 652165071Sdougbcfg_obj_asuint32(const cfg_obj_t *obj) { 653135446Strhodes REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32); 654135446Strhodes return (obj->value.uint32); 655135446Strhodes} 656135446Strhodes 657135446Strhodescfg_type_t cfg_type_uint32 = { 658135446Strhodes "integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal, 659135446Strhodes &cfg_rep_uint32, NULL 660135446Strhodes}; 661135446Strhodes 662135446Strhodes 663135446Strhodes/* 664135446Strhodes * uint64 665135446Strhodes */ 666135446Strhodesisc_boolean_t 667165071Sdougbcfg_obj_isuint64(const cfg_obj_t *obj) { 668135446Strhodes REQUIRE(obj != NULL); 669135446Strhodes return (ISC_TF(obj->type->rep == &cfg_rep_uint64)); 670135446Strhodes} 671135446Strhodes 672135446Strhodesisc_uint64_t 673165071Sdougbcfg_obj_asuint64(const cfg_obj_t *obj) { 674135446Strhodes REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64); 675135446Strhodes return (obj->value.uint64); 676135446Strhodes} 677135446Strhodes 678135446Strhodesvoid 679165071Sdougbcfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) { 680135446Strhodes char buf[32]; 681135446Strhodes snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u", 682135446Strhodes obj->value.uint64); 683135446Strhodes cfg_print_cstr(pctx, buf); 684135446Strhodes} 685135446Strhodes 686135446Strhodescfg_type_t cfg_type_uint64 = { 687135446Strhodes "64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal, 688135446Strhodes &cfg_rep_uint64, NULL 689135446Strhodes}; 690135446Strhodes 691135446Strhodes/* 692135446Strhodes * qstring (quoted string), ustring (unquoted string), astring 693135446Strhodes * (any string) 694135446Strhodes */ 695135446Strhodes 696135446Strhodes/* Create a string object from a null-terminated C string. */ 697135446Strhodesstatic isc_result_t 698135446Strhodescreate_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type, 699135446Strhodes cfg_obj_t **ret) 700135446Strhodes{ 701135446Strhodes isc_result_t result; 702135446Strhodes cfg_obj_t *obj = NULL; 703135446Strhodes int len; 704135446Strhodes 705135446Strhodes CHECK(cfg_create_obj(pctx, type, &obj)); 706135446Strhodes len = strlen(contents); 707135446Strhodes obj->value.string.length = len; 708135446Strhodes obj->value.string.base = isc_mem_get(pctx->mctx, len + 1); 709135446Strhodes if (obj->value.string.base == 0) { 710135446Strhodes isc_mem_put(pctx->mctx, obj, sizeof(*obj)); 711135446Strhodes return (ISC_R_NOMEMORY); 712135446Strhodes } 713262706Serwin memmove(obj->value.string.base, contents, len); 714135446Strhodes obj->value.string.base[len] = '\0'; 715135446Strhodes 716135446Strhodes *ret = obj; 717135446Strhodes cleanup: 718135446Strhodes return (result); 719135446Strhodes} 720135446Strhodes 721135446Strhodesisc_result_t 722135446Strhodescfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 723193149Sdougb isc_result_t result; 724135446Strhodes UNUSED(type); 725135446Strhodes 726135446Strhodes CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); 727135446Strhodes if (pctx->token.type != isc_tokentype_qstring) { 728135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string"); 729135446Strhodes return (ISC_R_UNEXPECTEDTOKEN); 730135446Strhodes } 731135446Strhodes return (create_string(pctx, 732135446Strhodes TOKEN_STRING(pctx), 733135446Strhodes &cfg_type_qstring, 734135446Strhodes ret)); 735135446Strhodes cleanup: 736135446Strhodes return (result); 737135446Strhodes} 738135446Strhodes 739135446Strhodesstatic isc_result_t 740135446Strhodesparse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 741193149Sdougb isc_result_t result; 742135446Strhodes UNUSED(type); 743135446Strhodes 744135446Strhodes CHECK(cfg_gettoken(pctx, 0)); 745135446Strhodes if (pctx->token.type != isc_tokentype_string) { 746135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string"); 747135446Strhodes return (ISC_R_UNEXPECTEDTOKEN); 748135446Strhodes } 749135446Strhodes return (create_string(pctx, 750135446Strhodes TOKEN_STRING(pctx), 751135446Strhodes &cfg_type_ustring, 752135446Strhodes ret)); 753135446Strhodes cleanup: 754135446Strhodes return (result); 755135446Strhodes} 756135446Strhodes 757135446Strhodesisc_result_t 758165071Sdougbcfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, 759165071Sdougb cfg_obj_t **ret) 760165071Sdougb{ 761193149Sdougb isc_result_t result; 762135446Strhodes UNUSED(type); 763135446Strhodes 764135446Strhodes CHECK(cfg_getstringtoken(pctx)); 765135446Strhodes return (create_string(pctx, 766135446Strhodes TOKEN_STRING(pctx), 767135446Strhodes &cfg_type_qstring, 768135446Strhodes ret)); 769135446Strhodes cleanup: 770135446Strhodes return (result); 771135446Strhodes} 772135446Strhodes 773262706Serwinisc_result_t 774262706Serwincfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type, 775262706Serwin cfg_obj_t **ret) 776262706Serwin{ 777262706Serwin isc_result_t result; 778262706Serwin UNUSED(type); 779262706Serwin 780262706Serwin CHECK(cfg_getstringtoken(pctx)); 781262706Serwin return (create_string(pctx, 782262706Serwin TOKEN_STRING(pctx), 783262706Serwin &cfg_type_sstring, 784262706Serwin ret)); 785262706Serwin cleanup: 786262706Serwin return (result); 787262706Serwin} 788262706Serwin 789135446Strhodesisc_boolean_t 790135446Strhodescfg_is_enum(const char *s, const char *const *enums) { 791135446Strhodes const char * const *p; 792135446Strhodes for (p = enums; *p != NULL; p++) { 793135446Strhodes if (strcasecmp(*p, s) == 0) 794135446Strhodes return (ISC_TRUE); 795135446Strhodes } 796135446Strhodes return (ISC_FALSE); 797135446Strhodes} 798135446Strhodes 799135446Strhodesstatic isc_result_t 800135446Strhodescheck_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) { 801135446Strhodes const char *s = obj->value.string.base; 802135446Strhodes if (cfg_is_enum(s, enums)) 803135446Strhodes return (ISC_R_SUCCESS); 804135446Strhodes cfg_parser_error(pctx, 0, "'%s' unexpected", s); 805135446Strhodes return (ISC_R_UNEXPECTEDTOKEN); 806135446Strhodes} 807135446Strhodes 808135446Strhodesisc_result_t 809135446Strhodescfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 810193149Sdougb isc_result_t result; 811135446Strhodes cfg_obj_t *obj = NULL; 812135446Strhodes CHECK(parse_ustring(pctx, NULL, &obj)); 813135446Strhodes CHECK(check_enum(pctx, obj, type->of)); 814135446Strhodes *ret = obj; 815135446Strhodes return (ISC_R_SUCCESS); 816135446Strhodes cleanup: 817193149Sdougb CLEANUP_OBJ(obj); 818135446Strhodes return (result); 819135446Strhodes} 820135446Strhodes 821135446Strhodesvoid 822135446Strhodescfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) { 823135446Strhodes const char * const *p; 824135446Strhodes cfg_print_chars(pctx, "( ", 2); 825135446Strhodes for (p = type->of; *p != NULL; p++) { 826135446Strhodes cfg_print_cstr(pctx, *p); 827135446Strhodes if (p[1] != NULL) 828135446Strhodes cfg_print_chars(pctx, " | ", 3); 829135446Strhodes } 830135446Strhodes cfg_print_chars(pctx, " )", 2); 831135446Strhodes} 832135446Strhodes 833135446Strhodesvoid 834165071Sdougbcfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) { 835135446Strhodes cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length); 836135446Strhodes} 837135446Strhodes 838135446Strhodesstatic void 839165071Sdougbprint_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) { 840135446Strhodes cfg_print_chars(pctx, "\"", 1); 841135446Strhodes cfg_print_ustring(pctx, obj); 842135446Strhodes cfg_print_chars(pctx, "\"", 1); 843135446Strhodes} 844135446Strhodes 845135446Strhodesstatic void 846262706Serwinprint_sstring(cfg_printer_t *pctx, const cfg_obj_t *obj) { 847262706Serwin cfg_print_chars(pctx, "\"", 1); 848262706Serwin if ((pctx->flags & CFG_PRINTER_XKEY) != 0) { 849262706Serwin unsigned int len = obj->value.string.length; 850262706Serwin while (len-- > 0) 851262706Serwin cfg_print_chars(pctx, "?", 1); 852262706Serwin } else 853262706Serwin cfg_print_ustring(pctx, obj); 854262706Serwin cfg_print_chars(pctx, "\"", 1); 855262706Serwin} 856262706Serwin 857262706Serwinstatic void 858135446Strhodesfree_string(cfg_parser_t *pctx, cfg_obj_t *obj) { 859135446Strhodes isc_mem_put(pctx->mctx, obj->value.string.base, 860135446Strhodes obj->value.string.length + 1); 861135446Strhodes} 862135446Strhodes 863135446Strhodesisc_boolean_t 864165071Sdougbcfg_obj_isstring(const cfg_obj_t *obj) { 865135446Strhodes REQUIRE(obj != NULL); 866135446Strhodes return (ISC_TF(obj->type->rep == &cfg_rep_string)); 867135446Strhodes} 868135446Strhodes 869165071Sdougbconst char * 870165071Sdougbcfg_obj_asstring(const cfg_obj_t *obj) { 871135446Strhodes REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string); 872135446Strhodes return (obj->value.string.base); 873135446Strhodes} 874135446Strhodes 875135446Strhodes/* Quoted string only */ 876135446Strhodescfg_type_t cfg_type_qstring = { 877135446Strhodes "quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal, 878135446Strhodes &cfg_rep_string, NULL 879135446Strhodes}; 880135446Strhodes 881135446Strhodes/* Unquoted string only */ 882135446Strhodescfg_type_t cfg_type_ustring = { 883135446Strhodes "string", parse_ustring, cfg_print_ustring, cfg_doc_terminal, 884135446Strhodes &cfg_rep_string, NULL 885135446Strhodes}; 886135446Strhodes 887135446Strhodes/* Any string (quoted or unquoted); printed with quotes */ 888135446Strhodescfg_type_t cfg_type_astring = { 889135446Strhodes "string", cfg_parse_astring, print_qstring, cfg_doc_terminal, 890135446Strhodes &cfg_rep_string, NULL 891135446Strhodes}; 892135446Strhodes 893135446Strhodes/* 894262706Serwin * Any string (quoted or unquoted); printed with quotes. 895262706Serwin * If CFG_PRINTER_XKEY is set when printing the string will be '?' out. 896262706Serwin */ 897262706Serwincfg_type_t cfg_type_sstring = { 898262706Serwin "string", cfg_parse_sstring, print_sstring, cfg_doc_terminal, 899262706Serwin &cfg_rep_string, NULL 900262706Serwin}; 901262706Serwin 902262706Serwin/* 903135446Strhodes * Booleans 904135446Strhodes */ 905135446Strhodes 906135446Strhodesisc_boolean_t 907165071Sdougbcfg_obj_isboolean(const cfg_obj_t *obj) { 908135446Strhodes REQUIRE(obj != NULL); 909135446Strhodes return (ISC_TF(obj->type->rep == &cfg_rep_boolean)); 910135446Strhodes} 911135446Strhodes 912135446Strhodesisc_boolean_t 913165071Sdougbcfg_obj_asboolean(const cfg_obj_t *obj) { 914135446Strhodes REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean); 915135446Strhodes return (obj->value.boolean); 916135446Strhodes} 917135446Strhodes 918224092Sdougbisc_result_t 919224092Sdougbcfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) 920135446Strhodes{ 921193149Sdougb isc_result_t result; 922135446Strhodes isc_boolean_t value; 923135446Strhodes cfg_obj_t *obj = NULL; 924135446Strhodes UNUSED(type); 925135446Strhodes 926135446Strhodes result = cfg_gettoken(pctx, 0); 927135446Strhodes if (result != ISC_R_SUCCESS) 928135446Strhodes return (result); 929135446Strhodes 930135446Strhodes if (pctx->token.type != isc_tokentype_string) 931135446Strhodes goto bad_boolean; 932135446Strhodes 933135446Strhodes if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) || 934135446Strhodes (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) || 935135446Strhodes (strcmp(TOKEN_STRING(pctx), "1") == 0)) { 936135446Strhodes value = ISC_TRUE; 937135446Strhodes } else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) || 938135446Strhodes (strcasecmp(TOKEN_STRING(pctx), "no") == 0) || 939135446Strhodes (strcmp(TOKEN_STRING(pctx), "0") == 0)) { 940135446Strhodes value = ISC_FALSE; 941135446Strhodes } else { 942135446Strhodes goto bad_boolean; 943135446Strhodes } 944135446Strhodes 945135446Strhodes CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj)); 946135446Strhodes obj->value.boolean = value; 947135446Strhodes *ret = obj; 948135446Strhodes return (result); 949135446Strhodes 950135446Strhodes bad_boolean: 951135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected"); 952135446Strhodes return (ISC_R_UNEXPECTEDTOKEN); 953135446Strhodes 954135446Strhodes cleanup: 955135446Strhodes return (result); 956135446Strhodes} 957135446Strhodes 958224092Sdougbvoid 959224092Sdougbcfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) { 960135446Strhodes if (obj->value.boolean) 961135446Strhodes cfg_print_chars(pctx, "yes", 3); 962135446Strhodes else 963135446Strhodes cfg_print_chars(pctx, "no", 2); 964135446Strhodes} 965135446Strhodes 966135446Strhodescfg_type_t cfg_type_boolean = { 967224092Sdougb "boolean", cfg_parse_boolean, cfg_print_boolean, cfg_doc_terminal, 968135446Strhodes &cfg_rep_boolean, NULL 969135446Strhodes}; 970135446Strhodes 971135446Strhodes/* 972135446Strhodes * Lists. 973135446Strhodes */ 974135446Strhodes 975135446Strhodesisc_result_t 976135446Strhodescfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) { 977135446Strhodes isc_result_t result; 978135446Strhodes CHECK(cfg_create_obj(pctx, type, obj)); 979135446Strhodes ISC_LIST_INIT((*obj)->value.list); 980135446Strhodes cleanup: 981135446Strhodes return (result); 982135446Strhodes} 983135446Strhodes 984135446Strhodesstatic isc_result_t 985135446Strhodescreate_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) { 986135446Strhodes cfg_listelt_t *elt; 987135446Strhodes elt = isc_mem_get(pctx->mctx, sizeof(*elt)); 988135446Strhodes if (elt == NULL) 989135446Strhodes return (ISC_R_NOMEMORY); 990135446Strhodes elt->obj = NULL; 991135446Strhodes ISC_LINK_INIT(elt, link); 992135446Strhodes *eltp = elt; 993135446Strhodes return (ISC_R_SUCCESS); 994135446Strhodes} 995135446Strhodes 996135446Strhodesstatic void 997135446Strhodesfree_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) { 998135446Strhodes cfg_obj_destroy(pctx, &elt->obj); 999135446Strhodes isc_mem_put(pctx->mctx, elt, sizeof(*elt)); 1000135446Strhodes} 1001135446Strhodes 1002135446Strhodesstatic void 1003135446Strhodesfree_list(cfg_parser_t *pctx, cfg_obj_t *obj) { 1004135446Strhodes cfg_listelt_t *elt, *next; 1005135446Strhodes for (elt = ISC_LIST_HEAD(obj->value.list); 1006135446Strhodes elt != NULL; 1007135446Strhodes elt = next) 1008135446Strhodes { 1009135446Strhodes next = ISC_LIST_NEXT(elt, link); 1010135446Strhodes free_list_elt(pctx, elt); 1011135446Strhodes } 1012135446Strhodes} 1013135446Strhodes 1014135446Strhodesisc_result_t 1015135446Strhodescfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype, 1016135446Strhodes cfg_listelt_t **ret) 1017135446Strhodes{ 1018135446Strhodes isc_result_t result; 1019135446Strhodes cfg_listelt_t *elt = NULL; 1020135446Strhodes cfg_obj_t *value = NULL; 1021135446Strhodes 1022135446Strhodes CHECK(create_listelt(pctx, &elt)); 1023135446Strhodes 1024135446Strhodes result = cfg_parse_obj(pctx, elttype, &value); 1025135446Strhodes if (result != ISC_R_SUCCESS) 1026135446Strhodes goto cleanup; 1027135446Strhodes 1028135446Strhodes elt->obj = value; 1029135446Strhodes 1030135446Strhodes *ret = elt; 1031135446Strhodes return (ISC_R_SUCCESS); 1032135446Strhodes 1033135446Strhodes cleanup: 1034135446Strhodes isc_mem_put(pctx->mctx, elt, sizeof(*elt)); 1035135446Strhodes return (result); 1036135446Strhodes} 1037135446Strhodes 1038135446Strhodes/* 1039135446Strhodes * Parse a homogeneous list whose elements are of type 'elttype' 1040135446Strhodes * and where each element is terminated by a semicolon. 1041135446Strhodes */ 1042135446Strhodesstatic isc_result_t 1043135446Strhodesparse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret) 1044135446Strhodes{ 1045135446Strhodes cfg_obj_t *listobj = NULL; 1046135446Strhodes const cfg_type_t *listof = listtype->of; 1047135446Strhodes isc_result_t result; 1048135446Strhodes cfg_listelt_t *elt = NULL; 1049135446Strhodes 1050135446Strhodes CHECK(cfg_create_list(pctx, listtype, &listobj)); 1051135446Strhodes 1052135446Strhodes for (;;) { 1053135446Strhodes CHECK(cfg_peektoken(pctx, 0)); 1054135446Strhodes if (pctx->token.type == isc_tokentype_special && 1055135446Strhodes pctx->token.value.as_char == /*{*/ '}') 1056135446Strhodes break; 1057135446Strhodes CHECK(cfg_parse_listelt(pctx, listof, &elt)); 1058135446Strhodes CHECK(parse_semicolon(pctx)); 1059135446Strhodes ISC_LIST_APPEND(listobj->value.list, elt, link); 1060135446Strhodes elt = NULL; 1061135446Strhodes } 1062135446Strhodes *ret = listobj; 1063135446Strhodes return (ISC_R_SUCCESS); 1064135446Strhodes 1065135446Strhodes cleanup: 1066135446Strhodes if (elt != NULL) 1067135446Strhodes free_list_elt(pctx, elt); 1068135446Strhodes CLEANUP_OBJ(listobj); 1069135446Strhodes return (result); 1070135446Strhodes} 1071135446Strhodes 1072135446Strhodesstatic void 1073165071Sdougbprint_list(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1074165071Sdougb const cfg_list_t *list = &obj->value.list; 1075165071Sdougb const cfg_listelt_t *elt; 1076135446Strhodes 1077135446Strhodes for (elt = ISC_LIST_HEAD(*list); 1078135446Strhodes elt != NULL; 1079135446Strhodes elt = ISC_LIST_NEXT(elt, link)) { 1080135446Strhodes print_indent(pctx); 1081135446Strhodes cfg_print_obj(pctx, elt->obj); 1082135446Strhodes cfg_print_chars(pctx, ";\n", 2); 1083135446Strhodes } 1084135446Strhodes} 1085135446Strhodes 1086135446Strhodesisc_result_t 1087135446Strhodescfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type, 1088135446Strhodes cfg_obj_t **ret) 1089135446Strhodes{ 1090135446Strhodes isc_result_t result; 1091135446Strhodes CHECK(cfg_parse_special(pctx, '{')); 1092135446Strhodes CHECK(parse_list(pctx, type, ret)); 1093135446Strhodes CHECK(cfg_parse_special(pctx, '}')); 1094135446Strhodes cleanup: 1095135446Strhodes return (result); 1096135446Strhodes} 1097135446Strhodes 1098135446Strhodesvoid 1099165071Sdougbcfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1100135446Strhodes print_open(pctx); 1101135446Strhodes print_list(pctx, obj); 1102135446Strhodes print_close(pctx); 1103135446Strhodes} 1104135446Strhodes 1105135446Strhodesvoid 1106135446Strhodescfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) { 1107135446Strhodes cfg_print_chars(pctx, "{ ", 2); 1108135446Strhodes cfg_doc_obj(pctx, type->of); 1109135446Strhodes cfg_print_chars(pctx, "; ... }", 7); 1110135446Strhodes} 1111135446Strhodes 1112135446Strhodes/* 1113135446Strhodes * Parse a homogeneous list whose elements are of type 'elttype' 1114135446Strhodes * and where elements are separated by space. The list ends 1115135446Strhodes * before the first semicolon. 1116135446Strhodes */ 1117135446Strhodesisc_result_t 1118135446Strhodescfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype, 1119135446Strhodes cfg_obj_t **ret) 1120135446Strhodes{ 1121135446Strhodes cfg_obj_t *listobj = NULL; 1122135446Strhodes const cfg_type_t *listof = listtype->of; 1123135446Strhodes isc_result_t result; 1124135446Strhodes 1125135446Strhodes CHECK(cfg_create_list(pctx, listtype, &listobj)); 1126135446Strhodes 1127135446Strhodes for (;;) { 1128135446Strhodes cfg_listelt_t *elt = NULL; 1129135446Strhodes 1130135446Strhodes CHECK(cfg_peektoken(pctx, 0)); 1131135446Strhodes if (pctx->token.type == isc_tokentype_special && 1132135446Strhodes pctx->token.value.as_char == ';') 1133135446Strhodes break; 1134135446Strhodes CHECK(cfg_parse_listelt(pctx, listof, &elt)); 1135135446Strhodes ISC_LIST_APPEND(listobj->value.list, elt, link); 1136135446Strhodes } 1137135446Strhodes *ret = listobj; 1138135446Strhodes return (ISC_R_SUCCESS); 1139135446Strhodes 1140135446Strhodes cleanup: 1141135446Strhodes CLEANUP_OBJ(listobj); 1142135446Strhodes return (result); 1143135446Strhodes} 1144135446Strhodes 1145135446Strhodesvoid 1146165071Sdougbcfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1147165071Sdougb const cfg_list_t *list = &obj->value.list; 1148165071Sdougb const cfg_listelt_t *elt; 1149135446Strhodes 1150135446Strhodes for (elt = ISC_LIST_HEAD(*list); 1151135446Strhodes elt != NULL; 1152135446Strhodes elt = ISC_LIST_NEXT(elt, link)) { 1153135446Strhodes cfg_print_obj(pctx, elt->obj); 1154135446Strhodes if (ISC_LIST_NEXT(elt, link) != NULL) 1155135446Strhodes cfg_print_chars(pctx, " ", 1); 1156135446Strhodes } 1157135446Strhodes} 1158135446Strhodes 1159135446Strhodesisc_boolean_t 1160165071Sdougbcfg_obj_islist(const cfg_obj_t *obj) { 1161135446Strhodes REQUIRE(obj != NULL); 1162135446Strhodes return (ISC_TF(obj->type->rep == &cfg_rep_list)); 1163135446Strhodes} 1164135446Strhodes 1165165071Sdougbconst cfg_listelt_t * 1166165071Sdougbcfg_list_first(const cfg_obj_t *obj) { 1167135446Strhodes REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list); 1168135446Strhodes if (obj == NULL) 1169135446Strhodes return (NULL); 1170135446Strhodes return (ISC_LIST_HEAD(obj->value.list)); 1171135446Strhodes} 1172135446Strhodes 1173165071Sdougbconst cfg_listelt_t * 1174165071Sdougbcfg_list_next(const cfg_listelt_t *elt) { 1175135446Strhodes REQUIRE(elt != NULL); 1176135446Strhodes return (ISC_LIST_NEXT(elt, link)); 1177135446Strhodes} 1178135446Strhodes 1179193149Sdougb/* 1180193149Sdougb * Return the length of a list object. If obj is NULL or is not 1181193149Sdougb * a list, return 0. 1182193149Sdougb */ 1183193149Sdougbunsigned int 1184193149Sdougbcfg_list_length(const cfg_obj_t *obj, isc_boolean_t recurse) { 1185193149Sdougb const cfg_listelt_t *elt; 1186193149Sdougb unsigned int count = 0; 1187193149Sdougb 1188193149Sdougb if (obj == NULL || !cfg_obj_islist(obj)) 1189193149Sdougb return (0U); 1190193149Sdougb for (elt = cfg_list_first(obj); 1191193149Sdougb elt != NULL; 1192193149Sdougb elt = cfg_list_next(elt)) { 1193193149Sdougb if (recurse && cfg_obj_islist(elt->obj)) { 1194193149Sdougb count += cfg_list_length(elt->obj, recurse); 1195193149Sdougb } else { 1196193149Sdougb count++; 1197193149Sdougb } 1198193149Sdougb } 1199193149Sdougb return (count); 1200193149Sdougb} 1201193149Sdougb 1202224092Sdougbcfg_obj_t * 1203165071Sdougbcfg_listelt_value(const cfg_listelt_t *elt) { 1204135446Strhodes REQUIRE(elt != NULL); 1205135446Strhodes return (elt->obj); 1206135446Strhodes} 1207135446Strhodes 1208135446Strhodes/* 1209135446Strhodes * Maps. 1210135446Strhodes */ 1211135446Strhodes 1212135446Strhodes/* 1213135446Strhodes * Parse a map body. That's something like 1214135446Strhodes * 1215135446Strhodes * "foo 1; bar { glub; }; zap true; zap false;" 1216135446Strhodes * 1217135446Strhodes * i.e., a sequence of option names followed by values and 1218135446Strhodes * terminated by semicolons. Used for the top level of 1219135446Strhodes * the named.conf syntax, as well as for the body of the 1220135446Strhodes * options, view, zone, and other statements. 1221135446Strhodes */ 1222135446Strhodesisc_result_t 1223135446Strhodescfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) 1224135446Strhodes{ 1225135446Strhodes const cfg_clausedef_t * const *clausesets = type->of; 1226135446Strhodes isc_result_t result; 1227135446Strhodes const cfg_clausedef_t * const *clauseset; 1228135446Strhodes const cfg_clausedef_t *clause; 1229135446Strhodes cfg_obj_t *value = NULL; 1230135446Strhodes cfg_obj_t *obj = NULL; 1231135446Strhodes cfg_obj_t *eltobj = NULL; 1232135446Strhodes cfg_obj_t *includename = NULL; 1233135446Strhodes isc_symvalue_t symval; 1234135446Strhodes cfg_list_t *list = NULL; 1235135446Strhodes 1236135446Strhodes CHECK(create_map(pctx, type, &obj)); 1237135446Strhodes 1238135446Strhodes obj->value.map.clausesets = clausesets; 1239135446Strhodes 1240135446Strhodes for (;;) { 1241135446Strhodes cfg_listelt_t *elt; 1242135446Strhodes 1243135446Strhodes redo: 1244135446Strhodes /* 1245135446Strhodes * Parse the option name and see if it is known. 1246135446Strhodes */ 1247135446Strhodes CHECK(cfg_gettoken(pctx, 0)); 1248135446Strhodes 1249135446Strhodes if (pctx->token.type != isc_tokentype_string) { 1250135446Strhodes cfg_ungettoken(pctx); 1251135446Strhodes break; 1252135446Strhodes } 1253135446Strhodes 1254135446Strhodes /* 1255135446Strhodes * We accept "include" statements wherever a map body 1256135446Strhodes * clause can occur. 1257135446Strhodes */ 1258135446Strhodes if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) { 1259135446Strhodes /* 1260135446Strhodes * Turn the file name into a temporary configuration 1261135446Strhodes * object just so that it is not overwritten by the 1262135446Strhodes * semicolon token. 1263135446Strhodes */ 1264135446Strhodes CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename)); 1265135446Strhodes CHECK(parse_semicolon(pctx)); 1266135446Strhodes CHECK(parser_openfile(pctx, includename-> 1267135446Strhodes value.string.base)); 1268135446Strhodes cfg_obj_destroy(pctx, &includename); 1269135446Strhodes goto redo; 1270135446Strhodes } 1271135446Strhodes 1272135446Strhodes clause = NULL; 1273135446Strhodes for (clauseset = clausesets; *clauseset != NULL; clauseset++) { 1274135446Strhodes for (clause = *clauseset; 1275135446Strhodes clause->name != NULL; 1276135446Strhodes clause++) { 1277135446Strhodes if (strcasecmp(TOKEN_STRING(pctx), 1278135446Strhodes clause->name) == 0) 1279135446Strhodes goto done; 1280135446Strhodes } 1281135446Strhodes } 1282135446Strhodes done: 1283135446Strhodes if (clause == NULL || clause->name == NULL) { 1284135446Strhodes cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option"); 1285135446Strhodes /* 1286135446Strhodes * Try to recover by parsing this option as an unknown 1287135446Strhodes * option and discarding it. 1288135446Strhodes */ 1289135446Strhodes CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj)); 1290135446Strhodes cfg_obj_destroy(pctx, &eltobj); 1291135446Strhodes CHECK(parse_semicolon(pctx)); 1292135446Strhodes continue; 1293135446Strhodes } 1294135446Strhodes 1295135446Strhodes /* Clause is known. */ 1296135446Strhodes 1297135446Strhodes /* Issue warnings if appropriate */ 1298135446Strhodes if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) 1299135446Strhodes cfg_parser_warning(pctx, 0, "option '%s' is obsolete", 1300135446Strhodes clause->name); 1301135446Strhodes if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0) 1302135446Strhodes cfg_parser_warning(pctx, 0, "option '%s' is " 1303135446Strhodes "not implemented", clause->name); 1304135446Strhodes if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0) 1305135446Strhodes cfg_parser_warning(pctx, 0, "option '%s' is " 1306135446Strhodes "not implemented", clause->name); 1307224092Sdougb 1308224092Sdougb if ((clause->flags & CFG_CLAUSEFLAG_NOTCONFIGURED) != 0) { 1309224092Sdougb cfg_parser_warning(pctx, 0, "option '%s' is not " 1310224092Sdougb "configured", clause->name); 1311224092Sdougb result = ISC_R_FAILURE; 1312224092Sdougb goto cleanup; 1313224092Sdougb } 1314224092Sdougb 1315135446Strhodes /* 1316135446Strhodes * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT 1317135446Strhodes * set here - we need to log the *lack* of such an option, 1318135446Strhodes * not its presence. 1319135446Strhodes */ 1320135446Strhodes 1321135446Strhodes /* See if the clause already has a value; if not create one. */ 1322135446Strhodes result = isc_symtab_lookup(obj->value.map.symtab, 1323135446Strhodes clause->name, 0, &symval); 1324135446Strhodes 1325135446Strhodes if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) { 1326135446Strhodes /* Multivalued clause */ 1327135446Strhodes cfg_obj_t *listobj = NULL; 1328135446Strhodes if (result == ISC_R_NOTFOUND) { 1329135446Strhodes CHECK(cfg_create_list(pctx, 1330135446Strhodes &cfg_type_implicitlist, 1331135446Strhodes &listobj)); 1332135446Strhodes symval.as_pointer = listobj; 1333135446Strhodes result = isc_symtab_define(obj->value. 1334135446Strhodes map.symtab, 1335135446Strhodes clause->name, 1336135446Strhodes 1, symval, 1337135446Strhodes isc_symexists_reject); 1338135446Strhodes if (result != ISC_R_SUCCESS) { 1339135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, 1340135446Strhodes "isc_symtab_define(%s) " 1341135446Strhodes "failed", clause->name); 1342135446Strhodes isc_mem_put(pctx->mctx, list, 1343135446Strhodes sizeof(cfg_list_t)); 1344135446Strhodes goto cleanup; 1345135446Strhodes } 1346135446Strhodes } else { 1347135446Strhodes INSIST(result == ISC_R_SUCCESS); 1348135446Strhodes listobj = symval.as_pointer; 1349135446Strhodes } 1350135446Strhodes 1351135446Strhodes elt = NULL; 1352135446Strhodes CHECK(cfg_parse_listelt(pctx, clause->type, &elt)); 1353135446Strhodes CHECK(parse_semicolon(pctx)); 1354135446Strhodes 1355135446Strhodes ISC_LIST_APPEND(listobj->value.list, elt, link); 1356135446Strhodes } else { 1357135446Strhodes /* Single-valued clause */ 1358135446Strhodes if (result == ISC_R_NOTFOUND) { 1359135446Strhodes isc_boolean_t callback = 1360135446Strhodes ISC_TF((clause->flags & 1361135446Strhodes CFG_CLAUSEFLAG_CALLBACK) != 0); 1362135446Strhodes CHECK(parse_symtab_elt(pctx, clause->name, 1363135446Strhodes clause->type, 1364135446Strhodes obj->value.map.symtab, 1365135446Strhodes callback)); 1366135446Strhodes CHECK(parse_semicolon(pctx)); 1367135446Strhodes } else if (result == ISC_R_SUCCESS) { 1368135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined", 1369135446Strhodes clause->name); 1370135446Strhodes result = ISC_R_EXISTS; 1371135446Strhodes goto cleanup; 1372135446Strhodes } else { 1373135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, 1374135446Strhodes "isc_symtab_define() failed"); 1375135446Strhodes goto cleanup; 1376135446Strhodes } 1377135446Strhodes } 1378135446Strhodes } 1379135446Strhodes 1380135446Strhodes 1381135446Strhodes *ret = obj; 1382135446Strhodes return (ISC_R_SUCCESS); 1383135446Strhodes 1384135446Strhodes cleanup: 1385135446Strhodes CLEANUP_OBJ(value); 1386135446Strhodes CLEANUP_OBJ(obj); 1387135446Strhodes CLEANUP_OBJ(eltobj); 1388135446Strhodes CLEANUP_OBJ(includename); 1389135446Strhodes return (result); 1390135446Strhodes} 1391135446Strhodes 1392135446Strhodesstatic isc_result_t 1393135446Strhodesparse_symtab_elt(cfg_parser_t *pctx, const char *name, 1394135446Strhodes cfg_type_t *elttype, isc_symtab_t *symtab, 1395135446Strhodes isc_boolean_t callback) 1396135446Strhodes{ 1397135446Strhodes isc_result_t result; 1398135446Strhodes cfg_obj_t *obj = NULL; 1399135446Strhodes isc_symvalue_t symval; 1400135446Strhodes 1401135446Strhodes CHECK(cfg_parse_obj(pctx, elttype, &obj)); 1402135446Strhodes 1403135446Strhodes if (callback && pctx->callback != NULL) 1404135446Strhodes CHECK(pctx->callback(name, obj, pctx->callbackarg)); 1405193149Sdougb 1406135446Strhodes symval.as_pointer = obj; 1407135446Strhodes CHECK(isc_symtab_define(symtab, name, 1408135446Strhodes 1, symval, 1409135446Strhodes isc_symexists_reject)); 1410135446Strhodes return (ISC_R_SUCCESS); 1411135446Strhodes 1412135446Strhodes cleanup: 1413135446Strhodes CLEANUP_OBJ(obj); 1414135446Strhodes return (result); 1415135446Strhodes} 1416135446Strhodes 1417135446Strhodes/* 1418135446Strhodes * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }" 1419135446Strhodes */ 1420135446Strhodesisc_result_t 1421135446Strhodescfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1422135446Strhodes isc_result_t result; 1423135446Strhodes CHECK(cfg_parse_special(pctx, '{')); 1424135446Strhodes CHECK(cfg_parse_mapbody(pctx, type, ret)); 1425135446Strhodes CHECK(cfg_parse_special(pctx, '}')); 1426135446Strhodes cleanup: 1427135446Strhodes return (result); 1428135446Strhodes} 1429135446Strhodes 1430135446Strhodes/* 1431135446Strhodes * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map(). 1432135446Strhodes */ 1433135446Strhodesstatic isc_result_t 1434135446Strhodesparse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type, 1435135446Strhodes cfg_obj_t **ret) 1436135446Strhodes{ 1437135446Strhodes isc_result_t result; 1438135446Strhodes cfg_obj_t *idobj = NULL; 1439135446Strhodes cfg_obj_t *mapobj = NULL; 1440135446Strhodes 1441135446Strhodes CHECK(cfg_parse_obj(pctx, nametype, &idobj)); 1442135446Strhodes CHECK(cfg_parse_map(pctx, type, &mapobj)); 1443135446Strhodes mapobj->value.map.id = idobj; 1444135446Strhodes idobj = NULL; 1445135446Strhodes *ret = mapobj; 1446135446Strhodes cleanup: 1447135446Strhodes CLEANUP_OBJ(idobj); 1448135446Strhodes return (result); 1449135446Strhodes} 1450135446Strhodes 1451135446Strhodes/* 1452193149Sdougb * Parse a map identified by a string name. E.g., "name { foo 1; }". 1453135446Strhodes * Used for the "key" and "channel" statements. 1454135446Strhodes */ 1455135446Strhodesisc_result_t 1456135446Strhodescfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1457135446Strhodes return (parse_any_named_map(pctx, &cfg_type_astring, type, ret)); 1458135446Strhodes} 1459135446Strhodes 1460135446Strhodes/* 1461135446Strhodes * Parse a map identified by a network address. 1462170222Sdougb * Used to be used for the "server" statement. 1463135446Strhodes */ 1464135446Strhodesisc_result_t 1465135446Strhodescfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1466135446Strhodes return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret)); 1467135446Strhodes} 1468135446Strhodes 1469170222Sdougb/* 1470170222Sdougb * Parse a map identified by a network prefix. 1471170222Sdougb * Used for the "server" statement. 1472170222Sdougb */ 1473170222Sdougbisc_result_t 1474170222Sdougbcfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1475170222Sdougb return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret)); 1476170222Sdougb} 1477170222Sdougb 1478135446Strhodesvoid 1479165071Sdougbcfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1480135446Strhodes isc_result_t result = ISC_R_SUCCESS; 1481135446Strhodes 1482135446Strhodes const cfg_clausedef_t * const *clauseset; 1483135446Strhodes 1484135446Strhodes for (clauseset = obj->value.map.clausesets; 1485135446Strhodes *clauseset != NULL; 1486135446Strhodes clauseset++) 1487135446Strhodes { 1488135446Strhodes isc_symvalue_t symval; 1489135446Strhodes const cfg_clausedef_t *clause; 1490135446Strhodes 1491135446Strhodes for (clause = *clauseset; 1492135446Strhodes clause->name != NULL; 1493135446Strhodes clause++) { 1494135446Strhodes result = isc_symtab_lookup(obj->value.map.symtab, 1495135446Strhodes clause->name, 0, &symval); 1496135446Strhodes if (result == ISC_R_SUCCESS) { 1497135446Strhodes cfg_obj_t *obj = symval.as_pointer; 1498135446Strhodes if (obj->type == &cfg_type_implicitlist) { 1499135446Strhodes /* Multivalued. */ 1500135446Strhodes cfg_list_t *list = &obj->value.list; 1501135446Strhodes cfg_listelt_t *elt; 1502135446Strhodes for (elt = ISC_LIST_HEAD(*list); 1503135446Strhodes elt != NULL; 1504135446Strhodes elt = ISC_LIST_NEXT(elt, link)) { 1505135446Strhodes print_indent(pctx); 1506135446Strhodes cfg_print_cstr(pctx, clause->name); 1507135446Strhodes cfg_print_chars(pctx, " ", 1); 1508135446Strhodes cfg_print_obj(pctx, elt->obj); 1509135446Strhodes cfg_print_chars(pctx, ";\n", 2); 1510135446Strhodes } 1511135446Strhodes } else { 1512135446Strhodes /* Single-valued. */ 1513135446Strhodes print_indent(pctx); 1514135446Strhodes cfg_print_cstr(pctx, clause->name); 1515135446Strhodes cfg_print_chars(pctx, " ", 1); 1516135446Strhodes cfg_print_obj(pctx, obj); 1517135446Strhodes cfg_print_chars(pctx, ";\n", 2); 1518135446Strhodes } 1519135446Strhodes } else if (result == ISC_R_NOTFOUND) { 1520135446Strhodes ; /* do nothing */ 1521135446Strhodes } else { 1522135446Strhodes INSIST(0); 1523135446Strhodes } 1524135446Strhodes } 1525135446Strhodes } 1526135446Strhodes} 1527135446Strhodes 1528135446Strhodesvoid 1529135446Strhodescfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) { 1530135446Strhodes const cfg_clausedef_t * const *clauseset; 1531135446Strhodes const cfg_clausedef_t *clause; 1532193149Sdougb 1533135446Strhodes for (clauseset = type->of; *clauseset != NULL; clauseset++) { 1534135446Strhodes for (clause = *clauseset; 1535135446Strhodes clause->name != NULL; 1536135446Strhodes clause++) { 1537135446Strhodes cfg_print_cstr(pctx, clause->name); 1538135446Strhodes cfg_print_chars(pctx, " ", 1); 1539135446Strhodes cfg_doc_obj(pctx, clause->type); 1540135446Strhodes cfg_print_chars(pctx, ";", 1); 1541135446Strhodes /* XXX print flags here? */ 1542135446Strhodes cfg_print_chars(pctx, "\n\n", 2); 1543135446Strhodes } 1544135446Strhodes } 1545135446Strhodes} 1546135446Strhodes 1547135446Strhodesstatic struct flagtext { 1548135446Strhodes unsigned int flag; 1549135446Strhodes const char *text; 1550135446Strhodes} flagtexts[] = { 1551135446Strhodes { CFG_CLAUSEFLAG_NOTIMP, "not implemented" }, 1552135446Strhodes { CFG_CLAUSEFLAG_NYI, "not yet implemented" }, 1553135446Strhodes { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" }, 1554135446Strhodes { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" }, 1555193149Sdougb { CFG_CLAUSEFLAG_TESTONLY, "test only" }, 1556224092Sdougb { CFG_CLAUSEFLAG_NOTCONFIGURED, "not configured" }, 1557135446Strhodes { 0, NULL } 1558135446Strhodes}; 1559135446Strhodes 1560135446Strhodesvoid 1561165071Sdougbcfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) { 1562135446Strhodes if (obj->value.map.id != NULL) { 1563135446Strhodes cfg_print_obj(pctx, obj->value.map.id); 1564135446Strhodes cfg_print_chars(pctx, " ", 1); 1565135446Strhodes } 1566135446Strhodes print_open(pctx); 1567135446Strhodes cfg_print_mapbody(pctx, obj); 1568135446Strhodes print_close(pctx); 1569135446Strhodes} 1570135446Strhodes 1571135446Strhodesstatic void 1572135446Strhodesprint_clause_flags(cfg_printer_t *pctx, unsigned int flags) { 1573135446Strhodes struct flagtext *p; 1574135446Strhodes isc_boolean_t first = ISC_TRUE; 1575135446Strhodes for (p = flagtexts; p->flag != 0; p++) { 1576135446Strhodes if ((flags & p->flag) != 0) { 1577135446Strhodes if (first) 1578135446Strhodes cfg_print_chars(pctx, " // ", 4); 1579135446Strhodes else 1580135446Strhodes cfg_print_chars(pctx, ", ", 2); 1581135446Strhodes cfg_print_cstr(pctx, p->text); 1582135446Strhodes first = ISC_FALSE; 1583135446Strhodes } 1584135446Strhodes } 1585135446Strhodes} 1586135446Strhodes 1587135446Strhodesvoid 1588135446Strhodescfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) { 1589135446Strhodes const cfg_clausedef_t * const *clauseset; 1590135446Strhodes const cfg_clausedef_t *clause; 1591193149Sdougb 1592135446Strhodes if (type->parse == cfg_parse_named_map) { 1593135446Strhodes cfg_doc_obj(pctx, &cfg_type_astring); 1594135446Strhodes cfg_print_chars(pctx, " ", 1); 1595135446Strhodes } else if (type->parse == cfg_parse_addressed_map) { 1596135446Strhodes cfg_doc_obj(pctx, &cfg_type_netaddr); 1597135446Strhodes cfg_print_chars(pctx, " ", 1); 1598170222Sdougb } else if (type->parse == cfg_parse_netprefix_map) { 1599170222Sdougb cfg_doc_obj(pctx, &cfg_type_netprefix); 1600170222Sdougb cfg_print_chars(pctx, " ", 1); 1601135446Strhodes } 1602193149Sdougb 1603135446Strhodes print_open(pctx); 1604193149Sdougb 1605135446Strhodes for (clauseset = type->of; *clauseset != NULL; clauseset++) { 1606135446Strhodes for (clause = *clauseset; 1607135446Strhodes clause->name != NULL; 1608135446Strhodes clause++) { 1609135446Strhodes print_indent(pctx); 1610135446Strhodes cfg_print_cstr(pctx, clause->name); 1611135446Strhodes if (clause->type->print != cfg_print_void) 1612135446Strhodes cfg_print_chars(pctx, " ", 1); 1613135446Strhodes cfg_doc_obj(pctx, clause->type); 1614135446Strhodes cfg_print_chars(pctx, ";", 1); 1615135446Strhodes print_clause_flags(pctx, clause->flags); 1616135446Strhodes cfg_print_chars(pctx, "\n", 1); 1617135446Strhodes } 1618135446Strhodes } 1619135446Strhodes print_close(pctx); 1620135446Strhodes} 1621135446Strhodes 1622135446Strhodesisc_boolean_t 1623165071Sdougbcfg_obj_ismap(const cfg_obj_t *obj) { 1624135446Strhodes REQUIRE(obj != NULL); 1625135446Strhodes return (ISC_TF(obj->type->rep == &cfg_rep_map)); 1626135446Strhodes} 1627135446Strhodes 1628135446Strhodesisc_result_t 1629165071Sdougbcfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) { 1630135446Strhodes isc_result_t result; 1631135446Strhodes isc_symvalue_t val; 1632165071Sdougb const cfg_map_t *map; 1633193149Sdougb 1634135446Strhodes REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 1635135446Strhodes REQUIRE(name != NULL); 1636135446Strhodes REQUIRE(obj != NULL && *obj == NULL); 1637135446Strhodes 1638135446Strhodes map = &mapobj->value.map; 1639193149Sdougb 1640135446Strhodes result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val); 1641135446Strhodes if (result != ISC_R_SUCCESS) 1642135446Strhodes return (result); 1643135446Strhodes *obj = val.as_pointer; 1644135446Strhodes return (ISC_R_SUCCESS); 1645135446Strhodes} 1646135446Strhodes 1647165071Sdougbconst cfg_obj_t * 1648165071Sdougbcfg_map_getname(const cfg_obj_t *mapobj) { 1649135446Strhodes REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map); 1650135446Strhodes return (mapobj->value.map.id); 1651135446Strhodes} 1652135446Strhodes 1653135446Strhodes 1654135446Strhodes/* Parse an arbitrary token, storing its raw text representation. */ 1655135446Strhodesstatic isc_result_t 1656135446Strhodesparse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1657135446Strhodes cfg_obj_t *obj = NULL; 1658193149Sdougb isc_result_t result; 1659135446Strhodes isc_region_t r; 1660135446Strhodes 1661135446Strhodes UNUSED(type); 1662135446Strhodes 1663135446Strhodes CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj)); 1664135446Strhodes CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING)); 1665135446Strhodes if (pctx->token.type == isc_tokentype_eof) { 1666135446Strhodes cfg_ungettoken(pctx); 1667135446Strhodes result = ISC_R_EOF; 1668135446Strhodes goto cleanup; 1669135446Strhodes } 1670135446Strhodes 1671135446Strhodes isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r); 1672135446Strhodes 1673135446Strhodes obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1); 1674165071Sdougb if (obj->value.string.base == NULL) { 1675165071Sdougb result = ISC_R_NOMEMORY; 1676165071Sdougb goto cleanup; 1677165071Sdougb } 1678135446Strhodes obj->value.string.length = r.length; 1679262706Serwin memmove(obj->value.string.base, r.base, r.length); 1680135446Strhodes obj->value.string.base[r.length] = '\0'; 1681135446Strhodes *ret = obj; 1682165071Sdougb return (result); 1683135446Strhodes 1684135446Strhodes cleanup: 1685165071Sdougb if (obj != NULL) 1686165071Sdougb isc_mem_put(pctx->mctx, obj, sizeof(*obj)); 1687135446Strhodes return (result); 1688135446Strhodes} 1689135446Strhodes 1690135446Strhodescfg_type_t cfg_type_token = { 1691135446Strhodes "token", parse_token, cfg_print_ustring, cfg_doc_terminal, 1692135446Strhodes &cfg_rep_string, NULL 1693135446Strhodes}; 1694135446Strhodes 1695135446Strhodes/* 1696135446Strhodes * An unsupported option. This is just a list of tokens with balanced braces 1697135446Strhodes * ending in a semicolon. 1698135446Strhodes */ 1699135446Strhodes 1700135446Strhodesstatic isc_result_t 1701135446Strhodesparse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1702135446Strhodes cfg_obj_t *listobj = NULL; 1703135446Strhodes isc_result_t result; 1704135446Strhodes int braces = 0; 1705135446Strhodes 1706135446Strhodes CHECK(cfg_create_list(pctx, type, &listobj)); 1707135446Strhodes 1708135446Strhodes for (;;) { 1709135446Strhodes cfg_listelt_t *elt = NULL; 1710135446Strhodes 1711135446Strhodes CHECK(cfg_peektoken(pctx, 0)); 1712135446Strhodes if (pctx->token.type == isc_tokentype_special) { 1713135446Strhodes if (pctx->token.value.as_char == '{') 1714135446Strhodes braces++; 1715135446Strhodes else if (pctx->token.value.as_char == '}') 1716135446Strhodes braces--; 1717135446Strhodes else if (pctx->token.value.as_char == ';') 1718135446Strhodes if (braces == 0) 1719135446Strhodes break; 1720135446Strhodes } 1721135446Strhodes if (pctx->token.type == isc_tokentype_eof || braces < 0) { 1722135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token"); 1723135446Strhodes result = ISC_R_UNEXPECTEDTOKEN; 1724135446Strhodes goto cleanup; 1725135446Strhodes } 1726135446Strhodes 1727135446Strhodes CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt)); 1728135446Strhodes ISC_LIST_APPEND(listobj->value.list, elt, link); 1729135446Strhodes } 1730135446Strhodes INSIST(braces == 0); 1731135446Strhodes *ret = listobj; 1732135446Strhodes return (ISC_R_SUCCESS); 1733135446Strhodes 1734135446Strhodes cleanup: 1735135446Strhodes CLEANUP_OBJ(listobj); 1736135446Strhodes return (result); 1737135446Strhodes} 1738135446Strhodes 1739135446Strhodescfg_type_t cfg_type_unsupported = { 1740135446Strhodes "unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal, 1741135446Strhodes &cfg_rep_list, NULL 1742135446Strhodes}; 1743135446Strhodes 1744135446Strhodes/* 1745135446Strhodes * Try interpreting the current token as a network address. 1746135446Strhodes * 1747135446Strhodes * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard 1748135446Strhodes * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set. The 1749193149Sdougb * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is 1750135446Strhodes * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set), 1751135446Strhodes * and the IPv6 wildcard address otherwise. 1752135446Strhodes */ 1753135446Strhodesstatic isc_result_t 1754135446Strhodestoken_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) { 1755135446Strhodes char *s; 1756135446Strhodes struct in_addr in4a; 1757135446Strhodes struct in6_addr in6a; 1758135446Strhodes 1759135446Strhodes if (pctx->token.type != isc_tokentype_string) 1760135446Strhodes return (ISC_R_UNEXPECTEDTOKEN); 1761135446Strhodes 1762135446Strhodes s = TOKEN_STRING(pctx); 1763135446Strhodes if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) { 1764135446Strhodes if ((flags & CFG_ADDR_V4OK) != 0) { 1765135446Strhodes isc_netaddr_any(na); 1766135446Strhodes return (ISC_R_SUCCESS); 1767135446Strhodes } else if ((flags & CFG_ADDR_V6OK) != 0) { 1768135446Strhodes isc_netaddr_any6(na); 1769135446Strhodes return (ISC_R_SUCCESS); 1770135446Strhodes } else { 1771135446Strhodes INSIST(0); 1772135446Strhodes } 1773135446Strhodes } else { 1774135446Strhodes if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) { 1775135446Strhodes if (inet_pton(AF_INET, s, &in4a) == 1) { 1776135446Strhodes isc_netaddr_fromin(na, &in4a); 1777135446Strhodes return (ISC_R_SUCCESS); 1778135446Strhodes } 1779135446Strhodes } 1780135446Strhodes if ((flags & CFG_ADDR_V4PREFIXOK) != 0 && 1781135446Strhodes strlen(s) <= 15U) { 1782135446Strhodes char buf[64]; 1783135446Strhodes int i; 1784135446Strhodes 1785135446Strhodes strcpy(buf, s); 1786135446Strhodes for (i = 0; i < 3; i++) { 1787135446Strhodes strcat(buf, ".0"); 1788135446Strhodes if (inet_pton(AF_INET, buf, &in4a) == 1) { 1789135446Strhodes isc_netaddr_fromin(na, &in4a); 1790135446Strhodes return (ISC_R_SUCCESS); 1791135446Strhodes } 1792135446Strhodes } 1793135446Strhodes } 1794135446Strhodes if ((flags & CFG_ADDR_V6OK) != 0 && 1795135446Strhodes strlen(s) <= 127U) { 1796135446Strhodes char buf[128]; /* see lib/bind9/getaddresses.c */ 1797135446Strhodes char *d; /* zone delimiter */ 1798135446Strhodes isc_uint32_t zone = 0; /* scope zone ID */ 1799135446Strhodes 1800135446Strhodes strcpy(buf, s); 1801135446Strhodes d = strchr(buf, '%'); 1802135446Strhodes if (d != NULL) 1803135446Strhodes *d = '\0'; 1804135446Strhodes 1805135446Strhodes if (inet_pton(AF_INET6, buf, &in6a) == 1) { 1806135446Strhodes if (d != NULL) { 1807135446Strhodes#ifdef ISC_PLATFORM_HAVESCOPEID 1808135446Strhodes isc_result_t result; 1809135446Strhodes 1810135446Strhodes result = isc_netscope_pton(AF_INET6, 1811135446Strhodes d + 1, 1812135446Strhodes &in6a, 1813135446Strhodes &zone); 1814135446Strhodes if (result != ISC_R_SUCCESS) 1815135446Strhodes return (result); 1816135446Strhodes#else 1817135446Strhodes return (ISC_R_BADADDRESSFORM); 1818135446Strhodes#endif 1819135446Strhodes } 1820135446Strhodes 1821135446Strhodes isc_netaddr_fromin6(na, &in6a); 1822135446Strhodes isc_netaddr_setzone(na, zone); 1823135446Strhodes return (ISC_R_SUCCESS); 1824135446Strhodes } 1825135446Strhodes } 1826135446Strhodes } 1827135446Strhodes return (ISC_R_UNEXPECTEDTOKEN); 1828135446Strhodes} 1829135446Strhodes 1830135446Strhodesisc_result_t 1831135446Strhodescfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) { 1832135446Strhodes isc_result_t result; 1833170222Sdougb const char *wild = ""; 1834170222Sdougb const char *prefix = ""; 1835170222Sdougb 1836135446Strhodes CHECK(cfg_gettoken(pctx, 0)); 1837135446Strhodes result = token_addr(pctx, flags, na); 1838170222Sdougb if (result == ISC_R_UNEXPECTEDTOKEN) { 1839170222Sdougb if ((flags & CFG_ADDR_WILDOK) != 0) 1840170222Sdougb wild = " or '*'"; 1841170222Sdougb if ((flags & CFG_ADDR_V4PREFIXOK) != 0) 1842170222Sdougb wild = " or IPv4 prefix"; 1843170222Sdougb if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK) 1844170222Sdougb cfg_parser_error(pctx, CFG_LOG_NEAR, 1845170222Sdougb "expected IPv4 address%s%s", 1846170222Sdougb prefix, wild); 1847170222Sdougb else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK) 1848170222Sdougb cfg_parser_error(pctx, CFG_LOG_NEAR, 1849170222Sdougb "expected IPv6 address%s%s", 1850170222Sdougb prefix, wild); 1851170222Sdougb else 1852170222Sdougb cfg_parser_error(pctx, CFG_LOG_NEAR, 1853170222Sdougb "expected IP address%s%s", 1854170222Sdougb prefix, wild); 1855170222Sdougb } 1856135446Strhodes cleanup: 1857135446Strhodes return (result); 1858135446Strhodes} 1859135446Strhodes 1860135446Strhodesisc_boolean_t 1861135446Strhodescfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) { 1862135446Strhodes isc_result_t result; 1863135446Strhodes isc_netaddr_t na_dummy; 1864135446Strhodes result = token_addr(pctx, flags, &na_dummy); 1865135446Strhodes return (ISC_TF(result == ISC_R_SUCCESS)); 1866135446Strhodes} 1867135446Strhodes 1868135446Strhodesisc_result_t 1869135446Strhodescfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) { 1870135446Strhodes isc_result_t result; 1871135446Strhodes 1872135446Strhodes CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER)); 1873135446Strhodes 1874135446Strhodes if ((flags & CFG_ADDR_WILDOK) != 0 && 1875135446Strhodes pctx->token.type == isc_tokentype_string && 1876135446Strhodes strcmp(TOKEN_STRING(pctx), "*") == 0) { 1877135446Strhodes *port = 0; 1878135446Strhodes return (ISC_R_SUCCESS); 1879135446Strhodes } 1880135446Strhodes if (pctx->token.type != isc_tokentype_number) { 1881135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, 1882135446Strhodes "expected port number or '*'"); 1883135446Strhodes return (ISC_R_UNEXPECTEDTOKEN); 1884135446Strhodes } 1885135446Strhodes if (pctx->token.value.as_ulong >= 65536U) { 1886135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, 1887135446Strhodes "port number out of range"); 1888135446Strhodes return (ISC_R_UNEXPECTEDTOKEN); 1889135446Strhodes } 1890135446Strhodes *port = (in_port_t)(pctx->token.value.as_ulong); 1891135446Strhodes return (ISC_R_SUCCESS); 1892135446Strhodes cleanup: 1893135446Strhodes return (result); 1894135446Strhodes} 1895135446Strhodes 1896135446Strhodesvoid 1897165071Sdougbcfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) { 1898135446Strhodes isc_result_t result; 1899135446Strhodes char text[128]; 1900135446Strhodes isc_buffer_t buf; 1901135446Strhodes 1902135446Strhodes isc_buffer_init(&buf, text, sizeof(text)); 1903135446Strhodes result = isc_netaddr_totext(na, &buf); 1904135446Strhodes RUNTIME_CHECK(result == ISC_R_SUCCESS); 1905135446Strhodes cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf)); 1906135446Strhodes} 1907135446Strhodes 1908135446Strhodes/* netaddr */ 1909135446Strhodes 1910170222Sdougbstatic unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK; 1911170222Sdougbstatic unsigned int netaddr4_flags = CFG_ADDR_V4OK; 1912170222Sdougbstatic unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK; 1913170222Sdougbstatic unsigned int netaddr6_flags = CFG_ADDR_V6OK; 1914170222Sdougbstatic unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK; 1915170222Sdougb 1916135446Strhodesstatic isc_result_t 1917135446Strhodesparse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 1918135446Strhodes isc_result_t result; 1919135446Strhodes cfg_obj_t *obj = NULL; 1920135446Strhodes isc_netaddr_t netaddr; 1921170222Sdougb unsigned int flags = *(const unsigned int *)type->of; 1922170222Sdougb 1923135446Strhodes CHECK(cfg_create_obj(pctx, type, &obj)); 1924170222Sdougb CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr)); 1925135446Strhodes isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0); 1926135446Strhodes *ret = obj; 1927135446Strhodes return (ISC_R_SUCCESS); 1928135446Strhodes cleanup: 1929135446Strhodes CLEANUP_OBJ(obj); 1930135446Strhodes return (result); 1931135446Strhodes} 1932135446Strhodes 1933170222Sdougbstatic void 1934170222Sdougbcfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) { 1935170222Sdougb const unsigned int *flagp = type->of; 1936170222Sdougb int n = 0; 1937170222Sdougb if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK) 1938170222Sdougb cfg_print_chars(pctx, "( ", 2); 1939170222Sdougb if (*flagp & CFG_ADDR_V4OK) { 1940170222Sdougb cfg_print_cstr(pctx, "<ipv4_address>"); 1941170222Sdougb n++; 1942170222Sdougb } 1943170222Sdougb if (*flagp & CFG_ADDR_V6OK) { 1944170222Sdougb if (n != 0) 1945170222Sdougb cfg_print_chars(pctx, " | ", 3); 1946170222Sdougb cfg_print_cstr(pctx, "<ipv6_address>"); 1947193149Sdougb n++; 1948170222Sdougb } 1949170222Sdougb if (*flagp & CFG_ADDR_WILDOK) { 1950170222Sdougb if (n != 0) 1951170222Sdougb cfg_print_chars(pctx, " | ", 3); 1952170222Sdougb cfg_print_chars(pctx, "*", 1); 1953170222Sdougb n++; 1954225361Sdougb POST(n); 1955170222Sdougb } 1956170222Sdougb if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK) 1957170222Sdougb cfg_print_chars(pctx, " )", 2); 1958170222Sdougb} 1959170222Sdougb 1960135446Strhodescfg_type_t cfg_type_netaddr = { 1961170222Sdougb "netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr, 1962170222Sdougb &cfg_rep_sockaddr, &netaddr_flags 1963135446Strhodes}; 1964135446Strhodes 1965170222Sdougbcfg_type_t cfg_type_netaddr4 = { 1966170222Sdougb "netaddr4", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr, 1967170222Sdougb &cfg_rep_sockaddr, &netaddr4_flags 1968170222Sdougb}; 1969170222Sdougb 1970170222Sdougbcfg_type_t cfg_type_netaddr4wild = { 1971170222Sdougb "netaddr4wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr, 1972170222Sdougb &cfg_rep_sockaddr, &netaddr4wild_flags 1973170222Sdougb}; 1974170222Sdougb 1975170222Sdougbcfg_type_t cfg_type_netaddr6 = { 1976170222Sdougb "netaddr6", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr, 1977170222Sdougb &cfg_rep_sockaddr, &netaddr6_flags 1978170222Sdougb}; 1979170222Sdougb 1980170222Sdougbcfg_type_t cfg_type_netaddr6wild = { 1981170222Sdougb "netaddr6wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr, 1982170222Sdougb &cfg_rep_sockaddr, &netaddr6wild_flags 1983170222Sdougb}; 1984170222Sdougb 1985135446Strhodes/* netprefix */ 1986135446Strhodes 1987135446Strhodesisc_result_t 1988135446Strhodescfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type, 1989135446Strhodes cfg_obj_t **ret) 1990135446Strhodes{ 1991135446Strhodes cfg_obj_t *obj = NULL; 1992135446Strhodes isc_result_t result; 1993135446Strhodes isc_netaddr_t netaddr; 1994225361Sdougb unsigned int addrlen = 0, prefixlen; 1995135446Strhodes UNUSED(type); 1996135446Strhodes 1997135446Strhodes CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK | 1998135446Strhodes CFG_ADDR_V6OK, &netaddr)); 1999135446Strhodes switch (netaddr.family) { 2000135446Strhodes case AF_INET: 2001135446Strhodes addrlen = 32; 2002135446Strhodes break; 2003135446Strhodes case AF_INET6: 2004135446Strhodes addrlen = 128; 2005135446Strhodes break; 2006135446Strhodes default: 2007135446Strhodes INSIST(0); 2008135446Strhodes break; 2009135446Strhodes } 2010135446Strhodes CHECK(cfg_peektoken(pctx, 0)); 2011135446Strhodes if (pctx->token.type == isc_tokentype_special && 2012135446Strhodes pctx->token.value.as_char == '/') { 2013135446Strhodes CHECK(cfg_gettoken(pctx, 0)); /* read "/" */ 2014135446Strhodes CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER)); 2015135446Strhodes if (pctx->token.type != isc_tokentype_number) { 2016135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, 2017135446Strhodes "expected prefix length"); 2018135446Strhodes return (ISC_R_UNEXPECTEDTOKEN); 2019135446Strhodes } 2020135446Strhodes prefixlen = pctx->token.value.as_ulong; 2021135446Strhodes if (prefixlen > addrlen) { 2022135446Strhodes cfg_parser_error(pctx, CFG_LOG_NOPREP, 2023135446Strhodes "invalid prefix length"); 2024135446Strhodes return (ISC_R_RANGE); 2025135446Strhodes } 2026135446Strhodes } else { 2027135446Strhodes prefixlen = addrlen; 2028135446Strhodes } 2029135446Strhodes CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj)); 2030135446Strhodes obj->value.netprefix.address = netaddr; 2031135446Strhodes obj->value.netprefix.prefixlen = prefixlen; 2032135446Strhodes *ret = obj; 2033135446Strhodes return (ISC_R_SUCCESS); 2034135446Strhodes cleanup: 2035135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix"); 2036135446Strhodes return (result); 2037135446Strhodes} 2038135446Strhodes 2039135446Strhodesstatic void 2040165071Sdougbprint_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) { 2041165071Sdougb const cfg_netprefix_t *p = &obj->value.netprefix; 2042165071Sdougb 2043135446Strhodes cfg_print_rawaddr(pctx, &p->address); 2044135446Strhodes cfg_print_chars(pctx, "/", 1); 2045135446Strhodes cfg_print_rawuint(pctx, p->prefixlen); 2046135446Strhodes} 2047135446Strhodes 2048135446Strhodesisc_boolean_t 2049165071Sdougbcfg_obj_isnetprefix(const cfg_obj_t *obj) { 2050135446Strhodes REQUIRE(obj != NULL); 2051135446Strhodes return (ISC_TF(obj->type->rep == &cfg_rep_netprefix)); 2052135446Strhodes} 2053135446Strhodes 2054135446Strhodesvoid 2055165071Sdougbcfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr, 2056225361Sdougb unsigned int *prefixlen) 2057225361Sdougb{ 2058135446Strhodes REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix); 2059225361Sdougb REQUIRE(netaddr != NULL); 2060225361Sdougb REQUIRE(prefixlen != NULL); 2061225361Sdougb 2062135446Strhodes *netaddr = obj->value.netprefix.address; 2063135446Strhodes *prefixlen = obj->value.netprefix.prefixlen; 2064135446Strhodes} 2065135446Strhodes 2066135446Strhodescfg_type_t cfg_type_netprefix = { 2067135446Strhodes "netprefix", cfg_parse_netprefix, print_netprefix, cfg_doc_terminal, 2068135446Strhodes &cfg_rep_netprefix, NULL 2069135446Strhodes}; 2070135446Strhodes 2071135446Strhodesstatic isc_result_t 2072135446Strhodesparse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type, 2073135446Strhodes int flags, cfg_obj_t **ret) 2074135446Strhodes{ 2075135446Strhodes isc_result_t result; 2076135446Strhodes isc_netaddr_t netaddr; 2077135446Strhodes in_port_t port = 0; 2078135446Strhodes cfg_obj_t *obj = NULL; 2079135446Strhodes 2080135446Strhodes CHECK(cfg_create_obj(pctx, type, &obj)); 2081135446Strhodes CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr)); 2082135446Strhodes CHECK(cfg_peektoken(pctx, 0)); 2083135446Strhodes if (pctx->token.type == isc_tokentype_string && 2084135446Strhodes strcasecmp(TOKEN_STRING(pctx), "port") == 0) { 2085135446Strhodes CHECK(cfg_gettoken(pctx, 0)); /* read "port" */ 2086135446Strhodes CHECK(cfg_parse_rawport(pctx, flags, &port)); 2087135446Strhodes } 2088135446Strhodes isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port); 2089135446Strhodes *ret = obj; 2090135446Strhodes return (ISC_R_SUCCESS); 2091135446Strhodes 2092135446Strhodes cleanup: 2093135446Strhodes CLEANUP_OBJ(obj); 2094135446Strhodes return (result); 2095135446Strhodes} 2096135446Strhodes 2097135446Strhodesstatic unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK; 2098135446Strhodescfg_type_t cfg_type_sockaddr = { 2099135446Strhodes "sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr, cfg_doc_sockaddr, 2100135446Strhodes &cfg_rep_sockaddr, &sockaddr_flags 2101135446Strhodes}; 2102135446Strhodes 2103135446Strhodesisc_result_t 2104135446Strhodescfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 2105135446Strhodes const unsigned int *flagp = type->of; 2106135446Strhodes return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret)); 2107135446Strhodes} 2108135446Strhodes 2109135446Strhodesvoid 2110165071Sdougbcfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) { 2111135446Strhodes isc_netaddr_t netaddr; 2112135446Strhodes in_port_t port; 2113135446Strhodes char buf[ISC_NETADDR_FORMATSIZE]; 2114135446Strhodes 2115135446Strhodes isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr); 2116135446Strhodes isc_netaddr_format(&netaddr, buf, sizeof(buf)); 2117135446Strhodes cfg_print_cstr(pctx, buf); 2118135446Strhodes port = isc_sockaddr_getport(&obj->value.sockaddr); 2119135446Strhodes if (port != 0) { 2120135446Strhodes cfg_print_chars(pctx, " port ", 6); 2121135446Strhodes cfg_print_rawuint(pctx, port); 2122135446Strhodes } 2123135446Strhodes} 2124135446Strhodes 2125135446Strhodesvoid 2126135446Strhodescfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) { 2127135446Strhodes const unsigned int *flagp = type->of; 2128135446Strhodes int n = 0; 2129135446Strhodes cfg_print_chars(pctx, "( ", 2); 2130135446Strhodes if (*flagp & CFG_ADDR_V4OK) { 2131135446Strhodes cfg_print_cstr(pctx, "<ipv4_address>"); 2132135446Strhodes n++; 2133135446Strhodes } 2134135446Strhodes if (*flagp & CFG_ADDR_V6OK) { 2135135446Strhodes if (n != 0) 2136135446Strhodes cfg_print_chars(pctx, " | ", 3); 2137135446Strhodes cfg_print_cstr(pctx, "<ipv6_address>"); 2138193149Sdougb n++; 2139135446Strhodes } 2140135446Strhodes if (*flagp & CFG_ADDR_WILDOK) { 2141135446Strhodes if (n != 0) 2142135446Strhodes cfg_print_chars(pctx, " | ", 3); 2143135446Strhodes cfg_print_chars(pctx, "*", 1); 2144135446Strhodes n++; 2145225361Sdougb POST(n); 2146135446Strhodes } 2147135446Strhodes cfg_print_chars(pctx, " ) ", 3); 2148135446Strhodes if (*flagp & CFG_ADDR_WILDOK) { 2149135446Strhodes cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]"); 2150135446Strhodes } else { 2151135446Strhodes cfg_print_cstr(pctx, "[ port <integer> ]"); 2152135446Strhodes } 2153135446Strhodes} 2154135446Strhodes 2155135446Strhodesisc_boolean_t 2156165071Sdougbcfg_obj_issockaddr(const cfg_obj_t *obj) { 2157135446Strhodes REQUIRE(obj != NULL); 2158135446Strhodes return (ISC_TF(obj->type->rep == &cfg_rep_sockaddr)); 2159135446Strhodes} 2160135446Strhodes 2161165071Sdougbconst isc_sockaddr_t * 2162165071Sdougbcfg_obj_assockaddr(const cfg_obj_t *obj) { 2163135446Strhodes REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr); 2164135446Strhodes return (&obj->value.sockaddr); 2165135446Strhodes} 2166135446Strhodes 2167135446Strhodesisc_result_t 2168135446Strhodescfg_gettoken(cfg_parser_t *pctx, int options) { 2169135446Strhodes isc_result_t result; 2170135446Strhodes 2171135446Strhodes if (pctx->seen_eof) 2172135446Strhodes return (ISC_R_SUCCESS); 2173135446Strhodes 2174135446Strhodes options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE); 2175135446Strhodes 2176135446Strhodes redo: 2177135446Strhodes pctx->token.type = isc_tokentype_unknown; 2178135446Strhodes result = isc_lex_gettoken(pctx->lexer, options, &pctx->token); 2179135446Strhodes pctx->ungotten = ISC_FALSE; 2180135446Strhodes pctx->line = isc_lex_getsourceline(pctx->lexer); 2181135446Strhodes 2182135446Strhodes switch (result) { 2183135446Strhodes case ISC_R_SUCCESS: 2184135446Strhodes if (pctx->token.type == isc_tokentype_eof) { 2185135446Strhodes result = isc_lex_close(pctx->lexer); 2186135446Strhodes INSIST(result == ISC_R_NOMORE || 2187135446Strhodes result == ISC_R_SUCCESS); 2188135446Strhodes 2189135446Strhodes if (isc_lex_getsourcename(pctx->lexer) != NULL) { 2190135446Strhodes /* 2191135446Strhodes * Closed an included file, not the main file. 2192135446Strhodes */ 2193135446Strhodes cfg_listelt_t *elt; 2194135446Strhodes elt = ISC_LIST_TAIL(pctx->open_files-> 2195135446Strhodes value.list); 2196135446Strhodes INSIST(elt != NULL); 2197135446Strhodes ISC_LIST_UNLINK(pctx->open_files-> 2198135446Strhodes value.list, elt, link); 2199135446Strhodes ISC_LIST_APPEND(pctx->closed_files-> 2200135446Strhodes value.list, elt, link); 2201135446Strhodes goto redo; 2202135446Strhodes } 2203135446Strhodes pctx->seen_eof = ISC_TRUE; 2204135446Strhodes } 2205135446Strhodes break; 2206135446Strhodes 2207135446Strhodes case ISC_R_NOSPACE: 2208135446Strhodes /* More understandable than "ran out of space". */ 2209135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big"); 2210135446Strhodes break; 2211135446Strhodes 2212135446Strhodes case ISC_R_IOERROR: 2213135446Strhodes cfg_parser_error(pctx, 0, "%s", 2214135446Strhodes isc_result_totext(result)); 2215135446Strhodes break; 2216135446Strhodes 2217135446Strhodes default: 2218135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, "%s", 2219135446Strhodes isc_result_totext(result)); 2220135446Strhodes break; 2221135446Strhodes } 2222135446Strhodes return (result); 2223135446Strhodes} 2224135446Strhodes 2225135446Strhodesvoid 2226135446Strhodescfg_ungettoken(cfg_parser_t *pctx) { 2227135446Strhodes if (pctx->seen_eof) 2228135446Strhodes return; 2229135446Strhodes isc_lex_ungettoken(pctx->lexer, &pctx->token); 2230135446Strhodes pctx->ungotten = ISC_TRUE; 2231135446Strhodes} 2232135446Strhodes 2233135446Strhodesisc_result_t 2234135446Strhodescfg_peektoken(cfg_parser_t *pctx, int options) { 2235135446Strhodes isc_result_t result; 2236135446Strhodes CHECK(cfg_gettoken(pctx, options)); 2237135446Strhodes cfg_ungettoken(pctx); 2238135446Strhodes cleanup: 2239135446Strhodes return (result); 2240135446Strhodes} 2241135446Strhodes 2242135446Strhodes/* 2243135446Strhodes * Get a string token, accepting both the quoted and the unquoted form. 2244135446Strhodes * Log an error if the next token is not a string. 2245135446Strhodes */ 2246135446Strhodesstatic isc_result_t 2247135446Strhodescfg_getstringtoken(cfg_parser_t *pctx) { 2248135446Strhodes isc_result_t result; 2249135446Strhodes 2250135446Strhodes result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING); 2251135446Strhodes if (result != ISC_R_SUCCESS) 2252135446Strhodes return (result); 2253135446Strhodes 2254135446Strhodes if (pctx->token.type != isc_tokentype_string && 2255135446Strhodes pctx->token.type != isc_tokentype_qstring) { 2256135446Strhodes cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string"); 2257135446Strhodes return (ISC_R_UNEXPECTEDTOKEN); 2258135446Strhodes } 2259135446Strhodes return (ISC_R_SUCCESS); 2260135446Strhodes} 2261135446Strhodes 2262135446Strhodesvoid 2263135446Strhodescfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) { 2264135446Strhodes va_list args; 2265135446Strhodes va_start(args, fmt); 2266135446Strhodes parser_complain(pctx, ISC_FALSE, flags, fmt, args); 2267135446Strhodes va_end(args); 2268135446Strhodes pctx->errors++; 2269135446Strhodes} 2270135446Strhodes 2271135446Strhodesvoid 2272135446Strhodescfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) { 2273135446Strhodes va_list args; 2274135446Strhodes va_start(args, fmt); 2275135446Strhodes parser_complain(pctx, ISC_TRUE, flags, fmt, args); 2276135446Strhodes va_end(args); 2277135446Strhodes pctx->warnings++; 2278135446Strhodes} 2279135446Strhodes 2280135446Strhodes#define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */ 2281135446Strhodes 2282236374Sdougbstatic isc_boolean_t 2283236374Sdougbhave_current_file(cfg_parser_t *pctx) { 2284236374Sdougb cfg_listelt_t *elt; 2285236374Sdougb if (pctx->open_files == NULL) 2286236374Sdougb return (ISC_FALSE); 2287236374Sdougb 2288236374Sdougb elt = ISC_LIST_TAIL(pctx->open_files->value.list); 2289236374Sdougb if (elt == NULL) 2290236374Sdougb return (ISC_FALSE); 2291236374Sdougb 2292236374Sdougb return (ISC_TRUE); 2293236374Sdougb} 2294236374Sdougb 2295135446Strhodesstatic char * 2296135446Strhodescurrent_file(cfg_parser_t *pctx) { 2297135446Strhodes static char none[] = "none"; 2298135446Strhodes cfg_listelt_t *elt; 2299135446Strhodes cfg_obj_t *fileobj; 2300135446Strhodes 2301236374Sdougb if (!have_current_file(pctx)) 2302135446Strhodes return (none); 2303236374Sdougb 2304135446Strhodes elt = ISC_LIST_TAIL(pctx->open_files->value.list); 2305236374Sdougb if (elt == NULL) /* shouldn't be possible, but... */ 2306135446Strhodes return (none); 2307135446Strhodes 2308135446Strhodes fileobj = elt->obj; 2309135446Strhodes INSIST(fileobj->type == &cfg_type_qstring); 2310135446Strhodes return (fileobj->value.string.base); 2311135446Strhodes} 2312135446Strhodes 2313135446Strhodesstatic void 2314135446Strhodesparser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning, 2315135446Strhodes unsigned int flags, const char *format, 2316135446Strhodes va_list args) 2317135446Strhodes{ 2318135446Strhodes char tokenbuf[MAX_LOG_TOKEN + 10]; 2319135446Strhodes static char where[ISC_DIR_PATHMAX + 100]; 2320135446Strhodes static char message[2048]; 2321135446Strhodes int level = ISC_LOG_ERROR; 2322135446Strhodes const char *prep = ""; 2323135446Strhodes size_t len; 2324135446Strhodes 2325135446Strhodes if (is_warning) 2326135446Strhodes level = ISC_LOG_WARNING; 2327135446Strhodes 2328236374Sdougb where[0] = '\0'; 2329236374Sdougb if (have_current_file(pctx)) 2330236374Sdougb snprintf(where, sizeof(where), "%s:%u: ", 2331236374Sdougb current_file(pctx), pctx->line); 2332135446Strhodes 2333135446Strhodes len = vsnprintf(message, sizeof(message), format, args); 2334135446Strhodes if (len >= sizeof(message)) 2335135446Strhodes FATAL_ERROR(__FILE__, __LINE__, 2336135446Strhodes "error message would overflow"); 2337135446Strhodes 2338135446Strhodes if ((flags & (CFG_LOG_NEAR|CFG_LOG_BEFORE|CFG_LOG_NOPREP)) != 0) { 2339135446Strhodes isc_region_t r; 2340135446Strhodes 2341135446Strhodes if (pctx->ungotten) 2342135446Strhodes (void)cfg_gettoken(pctx, 0); 2343135446Strhodes 2344135446Strhodes if (pctx->token.type == isc_tokentype_eof) { 2345135446Strhodes snprintf(tokenbuf, sizeof(tokenbuf), "end of file"); 2346135446Strhodes } else if (pctx->token.type == isc_tokentype_unknown) { 2347135446Strhodes flags = 0; 2348135446Strhodes tokenbuf[0] = '\0'; 2349135446Strhodes } else { 2350135446Strhodes isc_lex_getlasttokentext(pctx->lexer, 2351135446Strhodes &pctx->token, &r); 2352135446Strhodes if (r.length > MAX_LOG_TOKEN) 2353135446Strhodes snprintf(tokenbuf, sizeof(tokenbuf), 2354135446Strhodes "'%.*s...'", MAX_LOG_TOKEN, r.base); 2355135446Strhodes else 2356135446Strhodes snprintf(tokenbuf, sizeof(tokenbuf), 2357135446Strhodes "'%.*s'", (int)r.length, r.base); 2358135446Strhodes } 2359135446Strhodes 2360135446Strhodes /* Choose a preposition. */ 2361135446Strhodes if (flags & CFG_LOG_NEAR) 2362135446Strhodes prep = " near "; 2363135446Strhodes else if (flags & CFG_LOG_BEFORE) 2364135446Strhodes prep = " before "; 2365135446Strhodes else 2366135446Strhodes prep = " "; 2367135446Strhodes } else { 2368135446Strhodes tokenbuf[0] = '\0'; 2369135446Strhodes } 2370135446Strhodes isc_log_write(pctx->lctx, CAT, MOD, level, 2371135446Strhodes "%s%s%s%s", where, message, prep, tokenbuf); 2372135446Strhodes} 2373135446Strhodes 2374135446Strhodesvoid 2375165071Sdougbcfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level, 2376165071Sdougb const char *fmt, ...) { 2377135446Strhodes va_list ap; 2378135446Strhodes char msgbuf[2048]; 2379135446Strhodes 2380135446Strhodes if (! isc_log_wouldlog(lctx, level)) 2381135446Strhodes return; 2382135446Strhodes 2383135446Strhodes va_start(ap, fmt); 2384135446Strhodes 2385135446Strhodes vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); 2386135446Strhodes isc_log_write(lctx, CAT, MOD, level, 2387135446Strhodes "%s:%u: %s", 2388135446Strhodes obj->file == NULL ? "<unknown file>" : obj->file, 2389135446Strhodes obj->line, msgbuf); 2390135446Strhodes va_end(ap); 2391135446Strhodes} 2392135446Strhodes 2393135446Strhodesconst char * 2394165071Sdougbcfg_obj_file(const cfg_obj_t *obj) { 2395135446Strhodes return (obj->file); 2396135446Strhodes} 2397135446Strhodes 2398135446Strhodesunsigned int 2399165071Sdougbcfg_obj_line(const cfg_obj_t *obj) { 2400135446Strhodes return (obj->line); 2401135446Strhodes} 2402135446Strhodes 2403135446Strhodesisc_result_t 2404135446Strhodescfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 2405224092Sdougb isc_result_t result; 2406135446Strhodes cfg_obj_t *obj; 2407135446Strhodes 2408135446Strhodes obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t)); 2409135446Strhodes if (obj == NULL) 2410135446Strhodes return (ISC_R_NOMEMORY); 2411135446Strhodes obj->type = type; 2412135446Strhodes obj->file = current_file(pctx); 2413135446Strhodes obj->line = pctx->line; 2414224092Sdougb result = isc_refcount_init(&obj->references, 1); 2415224092Sdougb if (result != ISC_R_SUCCESS) { 2416224092Sdougb isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t)); 2417224092Sdougb return (result); 2418224092Sdougb } 2419135446Strhodes *ret = obj; 2420135446Strhodes return (ISC_R_SUCCESS); 2421135446Strhodes} 2422135446Strhodes 2423224092Sdougb 2424135446Strhodesstatic void 2425135446Strhodesmap_symtabitem_destroy(char *key, unsigned int type, 2426135446Strhodes isc_symvalue_t symval, void *userarg) 2427135446Strhodes{ 2428135446Strhodes cfg_obj_t *obj = symval.as_pointer; 2429135446Strhodes cfg_parser_t *pctx = (cfg_parser_t *)userarg; 2430135446Strhodes 2431135446Strhodes UNUSED(key); 2432135446Strhodes UNUSED(type); 2433135446Strhodes 2434135446Strhodes cfg_obj_destroy(pctx, &obj); 2435135446Strhodes} 2436135446Strhodes 2437135446Strhodes 2438135446Strhodesstatic isc_result_t 2439135446Strhodescreate_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { 2440135446Strhodes isc_result_t result; 2441135446Strhodes isc_symtab_t *symtab = NULL; 2442135446Strhodes cfg_obj_t *obj = NULL; 2443135446Strhodes 2444135446Strhodes CHECK(cfg_create_obj(pctx, type, &obj)); 2445135446Strhodes CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */ 2446135446Strhodes map_symtabitem_destroy, 2447135446Strhodes pctx, ISC_FALSE, &symtab)); 2448135446Strhodes obj->value.map.symtab = symtab; 2449135446Strhodes obj->value.map.id = NULL; 2450135446Strhodes 2451135446Strhodes *ret = obj; 2452135446Strhodes return (ISC_R_SUCCESS); 2453135446Strhodes 2454135446Strhodes cleanup: 2455135446Strhodes if (obj != NULL) 2456135446Strhodes isc_mem_put(pctx->mctx, obj, sizeof(*obj)); 2457135446Strhodes return (result); 2458135446Strhodes} 2459135446Strhodes 2460135446Strhodesstatic void 2461135446Strhodesfree_map(cfg_parser_t *pctx, cfg_obj_t *obj) { 2462135446Strhodes CLEANUP_OBJ(obj->value.map.id); 2463135446Strhodes isc_symtab_destroy(&obj->value.map.symtab); 2464135446Strhodes} 2465135446Strhodes 2466135446Strhodesisc_boolean_t 2467165071Sdougbcfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) { 2468135446Strhodes return (ISC_TF(obj->type == type)); 2469135446Strhodes} 2470135446Strhodes 2471135446Strhodes/* 2472135446Strhodes * Destroy 'obj', a configuration object created in 'pctx'. 2473135446Strhodes */ 2474135446Strhodesvoid 2475135446Strhodescfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) { 2476254402Serwin cfg_obj_t *obj; 2477224092Sdougb unsigned int refs; 2478224092Sdougb 2479254402Serwin REQUIRE(objp != NULL && *objp != NULL); 2480254402Serwin REQUIRE(pctx != NULL); 2481254402Serwin 2482254402Serwin obj = *objp; 2483254402Serwin 2484224092Sdougb isc_refcount_decrement(&obj->references, &refs); 2485224092Sdougb if (refs == 0) { 2486224092Sdougb obj->type->rep->free(pctx, obj); 2487224092Sdougb isc_refcount_destroy(&obj->references); 2488224092Sdougb isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t)); 2489224092Sdougb } 2490135446Strhodes *objp = NULL; 2491135446Strhodes} 2492135446Strhodes 2493224092Sdougbvoid 2494224092Sdougbcfg_obj_attach(cfg_obj_t *src, cfg_obj_t **dest) { 2495224092Sdougb REQUIRE(src != NULL); 2496224092Sdougb REQUIRE(dest != NULL && *dest == NULL); 2497224092Sdougb isc_refcount_increment(&src->references, NULL); 2498224092Sdougb *dest = src; 2499224092Sdougb} 2500224092Sdougb 2501135446Strhodesstatic void 2502135446Strhodesfree_noop(cfg_parser_t *pctx, cfg_obj_t *obj) { 2503135446Strhodes UNUSED(pctx); 2504135446Strhodes UNUSED(obj); 2505135446Strhodes} 2506135446Strhodes 2507135446Strhodesvoid 2508135446Strhodescfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) { 2509135446Strhodes type->doc(pctx, type); 2510135446Strhodes} 2511135446Strhodes 2512135446Strhodesvoid 2513135446Strhodescfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) { 2514135446Strhodes cfg_print_chars(pctx, "<", 1); 2515135446Strhodes cfg_print_cstr(pctx, type->name); 2516135446Strhodes cfg_print_chars(pctx, ">", 1); 2517135446Strhodes} 2518135446Strhodes 2519135446Strhodesvoid 2520135446Strhodescfg_print_grammar(const cfg_type_t *type, 2521135446Strhodes void (*f)(void *closure, const char *text, int textlen), 2522135446Strhodes void *closure) 2523135446Strhodes{ 2524135446Strhodes cfg_printer_t pctx; 2525135446Strhodes pctx.f = f; 2526135446Strhodes pctx.closure = closure; 2527135446Strhodes pctx.indent = 0; 2528262706Serwin pctx.flags = 0; 2529135446Strhodes cfg_doc_obj(&pctx, type); 2530135446Strhodes} 2531