/* * Copyright (C) 2000 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* $Id: confndc.c,v 1.18 2000/06/19 17:41:42 bwelling Exp $ */ /* ** options { ** [ default-server server_name; ] ** [ default-key key_name; ] ** }; ** ** server server_name { ** key key_name; ** [ host name_or_addr; ] ** }; ** ** key key_name { ** algorithm string; ** secret string; ** }; ** */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* Type keys for symtab lookup */ #define KEYWORD_SYM_TYPE 0x1 #define CLASS_SYM_TYPE 0x2 #define ACL_SYM_TYPE 0x3 #define CONF_MAX_IDENT 1024 typedef struct { isc_mem_t *themem; isc_lex_t *thelexer; isc_symtab_t *thekeywords; int errors; int warnings; isc_boolean_t debug_lexer; dns_c_ndcctx_t *thecontext; isc_uint32_t currtok; isc_uint32_t prevtok; char tokstr[CONF_MAX_IDENT]; char prevtokstr[CONF_MAX_IDENT]; isc_uint32_t intval; struct in_addr ip4addr; struct in6_addr ip6addr; } ndcpcontext; struct keywordtoken { const char *token; const int yaccval; }; /* * DATA */ #define L_ALGORITHM 1 #define L_DEFAULT_KEY 3 #define L_DEFAULT_SERVER 4 #define L_END_INCLUDE 5 #define L_END_INPUT 6 #define L_EOS 7 #define L_HOST 8 #define L_IP4ADDR 9 #define L_IP6ADDR 10 #define L_KEY 11 #define L_LBRACE 12 #define L_OPTIONS 13 #define L_QSTRING 14 #define L_QUOTE 15 #define L_RBRACE 16 #define L_SECRET 17 #define L_SERVER 18 #define L_STRING 20 #define L_INTEGER 21 static struct keywordtoken keyword_tokens[] = { { "{", L_LBRACE }, { "}", L_RBRACE }, { ";", L_EOS }, { "default-server", L_DEFAULT_SERVER }, { "default-key", L_DEFAULT_KEY }, { "key", L_KEY }, { "host", L_HOST }, { "algorithm", L_ALGORITHM }, { "secret", L_SECRET }, { "options", L_OPTIONS }, { "server", L_SERVER }, { NULL, 0 } }; /* This table contains all the L_* values that are not stored in any other * keywordtoken table. */ static struct keywordtoken misc_tokens[] = { { "", L_END_INCLUDE }, { "", L_END_INPUT }, { "", L_IP4ADDR }, { "", L_IP6ADDR }, { "", L_QSTRING }, { "", L_QUOTE }, { "", L_STRING }, }; static isc_result_t parse_file(ndcpcontext *pctx, dns_c_ndcctx_t **context); static isc_result_t parse_statement(ndcpcontext *pctx); static isc_result_t parse_options(ndcpcontext *pctx, dns_c_ndcopts_t **opts); static isc_result_t parse_serverstmt(ndcpcontext *pctx, dns_c_ndcserver_t **server); static isc_result_t parse_keystmt(ndcpcontext *pctx, dns_c_kdeflist_t *keys); static const char * keyword2str(isc_int32_t val); static isc_boolean_t eat(ndcpcontext *pctx, isc_uint32_t token); static isc_boolean_t eat_eos(ndcpcontext *pctx); static isc_boolean_t eat_lbrace(ndcpcontext *pctx); static isc_boolean_t eat_rbrace(ndcpcontext *pctx); static isc_boolean_t looking_at(ndcpcontext *pctx, isc_uint32_t token); static isc_boolean_t looking_at_anystring(ndcpcontext *pctx); static isc_result_t parser_setup(ndcpcontext *pctx, isc_mem_t *mem, const char *filename); static void parser_complain(isc_boolean_t is_warning, isc_boolean_t print_last_token, ndcpcontext *pctx, const char *format, va_list args); static void parser_error(ndcpcontext *pctx, isc_boolean_t lasttoken, const char *fmt, ...); static void parser_warn(ndcpcontext *pctx, isc_boolean_t lasttoken, const char *fmt, ...); static isc_boolean_t is_ip6addr(const char *string, struct in6_addr *addr); static isc_boolean_t is_ip4addr(const char *string, struct in_addr *addr); static isc_result_t getnexttoken(ndcpcontext *pctx); static void syntax_error(ndcpcontext *pctx, isc_uint32_t keyword); /* *********************************************************************** */ /* PUBLIC DATA STRUCTURE FUNCTIONS */ /* *********************************************************************** */ isc_result_t dns_c_ndcctx_new(isc_mem_t *mem, dns_c_ndcctx_t **ctx) { dns_c_ndcctx_t *newctx; REQUIRE(ctx != NULL); REQUIRE(*ctx == NULL); newctx = isc_mem_get(mem, sizeof *newctx); if (newctx == NULL) return (ISC_R_NOMEMORY); newctx->mem = mem; newctx->magic = DNS_C_NDCCTX_MAGIC; newctx->opts = NULL; newctx->servers = NULL; newctx->keys = NULL; *ctx = newctx; return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcctx_destroy(dns_c_ndcctx_t **ndcctx) { dns_c_ndcctx_t *ctx; isc_mem_t *mem; REQUIRE(ndcctx != NULL); ctx = *ndcctx; REQUIRE(DNS_C_NDCCTX_VALID(ctx)); mem = ctx->mem; ctx->mem = NULL; if (ctx->opts != NULL) dns_c_ndcopts_destroy(&ctx->opts); if (ctx->servers != NULL) dns_c_ndcserverlist_destroy(&ctx->servers); if (ctx->keys != NULL) dns_c_kdeflist_delete(&ctx->keys); ctx->magic = 0; isc_mem_put(mem, ctx, sizeof *ctx); *ndcctx = NULL; return (ISC_R_SUCCESS); } void dns_c_ndcctx_print(FILE *fp, dns_c_ndcctx_t *ctx) { REQUIRE(fp != NULL); REQUIRE(DNS_C_NDCCTX_VALID(ctx)); if (ctx->opts != NULL) dns_c_ndcopts_print(fp, ctx->opts); if (ctx->servers != NULL) dns_c_ndcserverlist_print(fp, ctx->servers); if (ctx->keys != NULL) dns_c_kdeflist_print(fp, 0, ctx->keys); } void dns_c_ndcopts_print(FILE *fp, dns_c_ndcopts_t *opts) { REQUIRE(fp != NULL); REQUIRE(DNS_C_NDCOPTIONS_VALID(opts)); fprintf(fp, "options {\n"); if (opts->defserver != NULL) fprintf(fp, "\tdefault-server %s;\n", opts->defserver); if (opts->defkey != NULL) fprintf(fp, "\tdefault-key %s;\n", opts->defkey); fprintf(fp, "};\n\n\n"); } void dns_c_ndcserverlist_print(FILE *fp, dns_c_ndcserverlist_t *servers) { dns_c_ndcserver_t *server; REQUIRE(DNS_C_NDCSERVERLIST_VALID(servers)); REQUIRE(fp != NULL); server = dns_c_ndcserverlist_first(servers); while (server != NULL) { dns_c_ndcserver_print(fp, server); server = dns_c_ndcserverlist_next(server); } } void dns_c_ndcserver_print(FILE *fp, dns_c_ndcserver_t *server) { fprintf(fp, "server %s {\n", server->name); if (server->key != NULL) fprintf(fp, "\tkey %s;\n", server->key); if (server->host != NULL) fprintf(fp, "\thost %s;\n", server->host); fprintf(fp, "};\n\n\n"); } isc_result_t dns_c_ndcctx_setoptions(dns_c_ndcctx_t *ctx, dns_c_ndcopts_t *opts) { isc_boolean_t existed; REQUIRE(DNS_C_NDCCTX_VALID(ctx)); REQUIRE(opts == NULL || DNS_C_NDCOPTIONS_VALID(opts)); existed = ISC_TF(ctx->opts != NULL); ctx->opts = opts; if (existed) return (ISC_R_EXISTS); else return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcctx_getoptions(dns_c_ndcctx_t *ctx, dns_c_ndcopts_t **opts) { REQUIRE(DNS_C_NDCCTX_VALID(ctx)); REQUIRE(opts != NULL); REQUIRE(*opts == NULL); *opts = ctx->opts; if (ctx->opts == NULL) return (ISC_R_NOTFOUND); else return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcctx_setservers(dns_c_ndcctx_t *ctx, dns_c_ndcserverlist_t *servers) { isc_boolean_t existed; REQUIRE(DNS_C_NDCCTX_VALID(ctx)); REQUIRE(servers == NULL || DNS_C_NDCSERVERLIST_VALID(servers)); existed = ISC_TF(ctx->servers != NULL); ctx->servers = servers; if (existed) return (ISC_R_EXISTS); else return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcctx_getservers(dns_c_ndcctx_t *ctx, dns_c_ndcserverlist_t **servers) { REQUIRE(DNS_C_NDCCTX_VALID(ctx)); REQUIRE(servers != NULL); REQUIRE(*servers == NULL); *servers = ctx->servers; if (ctx->servers == NULL) return (ISC_R_NOTFOUND); else return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcctx_addserver(dns_c_ndcctx_t *ctx, dns_c_ndcserver_t **server) { isc_result_t result; REQUIRE(DNS_C_NDCCTX_VALID(ctx)); REQUIRE(server != NULL); REQUIRE(DNS_C_NDCSERVER_VALID(*server)); if (ctx->servers == NULL) { result = dns_c_ndcserverlist_new(ctx->mem, &ctx->servers); if (result != ISC_R_SUCCESS) return (result); } ISC_LIST_APPEND(ctx->servers->list, *server, next); *server = NULL; return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcctx_getserver(dns_c_ndcctx_t *ctx, const char *name, dns_c_ndcserver_t **server) { dns_c_ndcserver_t *s; REQUIRE(DNS_C_NDCCTX_VALID(ctx)); REQUIRE(name != NULL); REQUIRE(server != NULL && *server == NULL); if (ctx->servers != NULL) { for (s = ISC_LIST_HEAD(ctx->servers->list); s != NULL; s = ISC_LIST_NEXT(s, next)) { INSIST(s->name != NULL); if (strcasecmp(s->name, name) == 0) { *server = s; return (ISC_R_SUCCESS); } } } return (ISC_R_NOTFOUND); } isc_result_t dns_c_ndcctx_getkeys(dns_c_ndcctx_t *ctx, dns_c_kdeflist_t **keys) { REQUIRE(DNS_C_NDCCTX_VALID(ctx)); REQUIRE(keys != NULL); REQUIRE(*keys == NULL); *keys = ctx->keys; if (ctx->keys == NULL) return (ISC_R_NOTFOUND); else return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcctx_setkeys(dns_c_ndcctx_t *ctx, dns_c_kdeflist_t *keys) { isc_boolean_t existed; REQUIRE(DNS_C_NDCCTX_VALID(ctx)); REQUIRE(DNS_C_KDEFLIST_VALID(keys)); existed = ISC_TF(ctx->keys != NULL); ctx->keys = keys; if (existed) return (ISC_R_EXISTS); else return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcserverlist_new(isc_mem_t *mem, dns_c_ndcserverlist_t **servers) { dns_c_ndcserverlist_t *newlist; REQUIRE(servers != NULL); REQUIRE(*servers == NULL); newlist = isc_mem_get(mem, sizeof *newlist); if (newlist == NULL) return (ISC_R_NOMEMORY); newlist->mem = mem; newlist->magic = DNS_C_NDCSERVERLIST_MAGIC; ISC_LIST_INIT(newlist->list); *servers = newlist; return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcserverlist_destroy(dns_c_ndcserverlist_t **servers) { dns_c_ndcserverlist_t *slist; dns_c_ndcserver_t *server; dns_c_ndcserver_t *p; isc_mem_t *mem; REQUIRE(servers != NULL); slist = *servers; REQUIRE(DNS_C_NDCSERVERLIST_VALID(slist)); server = ISC_LIST_HEAD(slist->list); while (server != NULL) { p = ISC_LIST_NEXT(server, next); ISC_LIST_UNLINK(slist->list, server, next); dns_c_ndcserver_destroy(&server); server = p; } mem = slist->mem; slist->mem = NULL; slist->magic = 0; isc_mem_put(mem, slist, sizeof *slist); return (ISC_R_SUCCESS); } dns_c_ndcserver_t * dns_c_ndcserverlist_first(dns_c_ndcserverlist_t *servers) { REQUIRE(DNS_C_NDCSERVERLIST_VALID(servers)); return (ISC_LIST_HEAD(servers->list)); } dns_c_ndcserver_t * dns_c_ndcserverlist_next(dns_c_ndcserver_t *server) { REQUIRE(DNS_C_NDCSERVER_VALID(server)); return (ISC_LIST_NEXT(server, next)); } isc_result_t dns_c_ndcopts_new(isc_mem_t *mem, dns_c_ndcopts_t **opts) { dns_c_ndcopts_t *newo; REQUIRE(opts != NULL); REQUIRE(*opts == NULL); newo = isc_mem_get(mem, sizeof *newo); if (newo == NULL) return (ISC_R_NOMEMORY); newo->magic = DNS_C_NDCOPTIONS_MAGIC; newo->mem = mem; newo->defserver = NULL; newo->defkey = NULL; *opts = newo; return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcopts_destroy(dns_c_ndcopts_t **opts) { dns_c_ndcopts_t *o; isc_mem_t *mem; REQUIRE(opts != NULL); o = *opts; REQUIRE(DNS_C_NDCOPTIONS_VALID(o)); if (o->defserver != NULL) isc_mem_free(o->mem, o->defserver); if (o->defkey != NULL) isc_mem_free(o->mem, o->defkey); mem = o->mem; o->mem = NULL; o->magic = 0; isc_mem_put(mem, o, sizeof *o); return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcopts_getdefserver(dns_c_ndcopts_t *opts, const char **retval) { REQUIRE(DNS_C_NDCOPTIONS_VALID(opts)); REQUIRE(retval != NULL); REQUIRE(*retval == NULL); *retval = opts->defserver; if (opts->defserver == NULL) return (ISC_R_NOTFOUND); else return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcopts_getdefkey(dns_c_ndcopts_t *opts, const char **retval) { REQUIRE(DNS_C_NDCOPTIONS_VALID(opts)); REQUIRE(retval != NULL); REQUIRE(*retval == NULL); *retval = opts->defkey; if (opts->defkey == NULL) return (ISC_R_NOTFOUND); else return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcopts_setdefserver(dns_c_ndcopts_t *opts, const char *newval) { isc_boolean_t existed; REQUIRE(DNS_C_NDCOPTIONS_VALID(opts)); REQUIRE(newval == NULL || *newval != '\0'); existed = ISC_TF(opts->defserver != NULL); if (newval != NULL) { opts->defserver = isc_mem_strdup(opts->mem, newval); if (opts->defserver == NULL) return (ISC_R_NOMEMORY); } else opts->defserver = NULL; if (existed) return (ISC_R_EXISTS); else return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcopts_setdefkey(dns_c_ndcopts_t *opts, const char *newval) { isc_boolean_t existed; REQUIRE(DNS_C_NDCOPTIONS_VALID(opts)); REQUIRE(newval == NULL || *newval != '\0'); existed = ISC_TF(opts->defkey != NULL); if (newval != NULL) { opts->defkey = isc_mem_strdup(opts->mem, newval); if (opts->defkey == NULL) return (ISC_R_NOMEMORY); } else opts->defkey = NULL; if (existed) return (ISC_R_EXISTS); else return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcserver_new(isc_mem_t *mem, dns_c_ndcserver_t **server) { dns_c_ndcserver_t *serv = NULL; REQUIRE(server != NULL); REQUIRE(*server == NULL); serv = isc_mem_get(mem, sizeof *serv); if (serv == NULL) return (ISC_R_NOMEMORY); serv->magic = DNS_C_NDCSERVER_MAGIC; serv->mem = mem; serv->name = NULL; serv->key = NULL; serv->host = NULL; ISC_LINK_INIT(serv, next); *server = serv; return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcserver_destroy(dns_c_ndcserver_t **server) { dns_c_ndcserver_t *serv; isc_mem_t *mem; REQUIRE(server != NULL); serv = *server ; REQUIRE(DNS_C_NDCSERVER_VALID(serv)); if (serv->name != NULL) isc_mem_free(serv->mem, serv->name); if (serv->key != NULL) isc_mem_free(serv->mem, serv->key); if (serv->host != NULL) isc_mem_free(serv->mem, serv->host); mem = serv->mem; serv->mem = NULL; serv->magic = 0; isc_mem_put(mem, serv, sizeof *serv); return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcserver_setkey(dns_c_ndcserver_t *server, const char *val) { isc_boolean_t existed; REQUIRE(DNS_C_NDCSERVER_VALID(server)); existed = ISC_TF(server->key != NULL); if (val != NULL) { server->key = isc_mem_strdup(server->mem, val); if (server->key == NULL) return (ISC_R_NOMEMORY); } else server->key = NULL; if (existed) return (ISC_R_EXISTS); else return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcserver_setname(dns_c_ndcserver_t *server, const char *val) { isc_boolean_t existed; REQUIRE(DNS_C_NDCSERVER_VALID(server)); existed = ISC_TF(server->name != NULL); if (val != NULL) { server->name = isc_mem_strdup(server->mem, val); if (server->name == NULL) return (ISC_R_NOMEMORY); } else server->name = NULL; if (existed) return (ISC_R_EXISTS); else return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcserver_sethost(dns_c_ndcserver_t *server, const char *val) { isc_boolean_t existed; REQUIRE(DNS_C_NDCSERVER_VALID(server)); existed = ISC_TF(server->host != NULL); if (val != NULL) { server->host = isc_mem_strdup(server->mem, val); if (server->host == NULL) return (ISC_R_NOMEMORY); } else server->host = NULL; if (existed) return (ISC_R_EXISTS); else return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcserver_getkey(dns_c_ndcserver_t *server, const char **val) { REQUIRE(DNS_C_NDCSERVER_VALID(server)); REQUIRE(val != NULL); REQUIRE (*val == NULL); *val = server->key; if (server->key == NULL) return (ISC_R_NOTFOUND); else return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcserver_gethost(dns_c_ndcserver_t *server, const char **val) { REQUIRE(DNS_C_NDCSERVER_VALID(server)); REQUIRE(val != NULL); REQUIRE (*val == NULL); *val = server->host; if (server->host == NULL) return (ISC_R_NOTFOUND); else return (ISC_R_SUCCESS); } isc_result_t dns_c_ndcserver_getname(dns_c_ndcserver_t *server, const char **val) { REQUIRE(DNS_C_NDCSERVER_VALID(server)); REQUIRE(val != NULL); REQUIRE (*val == NULL); *val = server->name; if (server->name == NULL) return (ISC_R_NOTFOUND); else return (ISC_R_SUCCESS); } /* *********************************************************************** */ /* PUBLIC PARSING ROUTINE */ /* *********************************************************************** */ isc_result_t dns_c_ndcparseconf(const char *filename, isc_mem_t *mem, dns_c_ndcctx_t **ndcctx) { ndcpcontext pctx; isc_result_t result; dns_c_ndcctx_t *aConfig; result = parser_setup(&pctx, mem, filename); if (result != ISC_R_SUCCESS) goto done; result = parse_file(&pctx, &aConfig); if (result != ISC_R_SUCCESS && aConfig != NULL) dns_c_ndcctx_destroy(&aConfig); done: if (pctx.thelexer != NULL) isc_lex_destroy(&pctx.thelexer); if (pctx.thekeywords != NULL) isc_symtab_destroy(&pctx.thekeywords); *ndcctx = aConfig; return (result); } /* *********************************************************************** */ /* PRIVATE PARSING ROUTINES */ /* *********************************************************************** */ static isc_result_t parse_file(ndcpcontext *pctx, dns_c_ndcctx_t **context) { isc_result_t result; isc_boolean_t done = ISC_FALSE; result = dns_c_ndcctx_new(pctx->themem, context); if (result != ISC_R_SUCCESS) return (result); pctx->thecontext = *context; result = getnexttoken(pctx); done = ISC_TF(result != ISC_R_SUCCESS); while (!done) { switch (pctx->currtok) { case L_END_INPUT: result = ISC_R_SUCCESS; done = ISC_TRUE; break; default: result = parse_statement(pctx); if (result != ISC_R_SUCCESS) { done = ISC_TRUE; break; } } } return (result); } static isc_result_t parse_statement(ndcpcontext *pctx) { isc_result_t result; dns_c_ndcctx_t *ctx = pctx->thecontext; dns_c_ndcopts_t *opts = NULL; dns_c_ndcopts_t *tmpopts = NULL; dns_c_ndcserver_t *server = NULL; dns_c_kdeflist_t *keys = NULL; switch (pctx->currtok) { case L_OPTIONS: result = parse_options(pctx, &opts); if (result == ISC_R_SUCCESS) { (void)dns_c_ndcctx_getoptions(ctx, &tmpopts); result = dns_c_ndcctx_setoptions(ctx, opts); if (result == ISC_R_EXISTS) { parser_warn(pctx, ISC_FALSE, "redefining 'options'"); result = ISC_R_SUCCESS; dns_c_ndcopts_destroy(&tmpopts); } opts = NULL; } break; case L_SERVER: result = parse_serverstmt(pctx, &server); if (result == ISC_R_SUCCESS) result = dns_c_ndcctx_addserver(ctx, &server); break; case L_KEY: keys = NULL; result = dns_c_ndcctx_getkeys(ctx, &keys); if (result == ISC_R_NOTFOUND) { result = dns_c_kdeflist_new(pctx->themem, &keys); if (result != ISC_R_SUCCESS) return (result); dns_c_ndcctx_setkeys(ctx, keys); } result = parse_keystmt(pctx, keys); break; default: syntax_error(pctx, pctx->currtok); result = ISC_R_FAILURE; break; } if (result == ISC_R_SUCCESS) if (!eat_eos(pctx)) result = ISC_R_FAILURE; if (server != NULL) dns_c_ndcserver_destroy(&server); if (opts != NULL) dns_c_ndcopts_destroy(&opts); return (result); } static isc_result_t parse_options(ndcpcontext *pctx, dns_c_ndcopts_t **opts) { isc_result_t result; isc_uint32_t option; dns_c_ndcopts_t *newopts = NULL; dns_c_ndcctx_t *cfgctx = pctx->thecontext; REQUIRE(DNS_C_NDCCTX_VALID(cfgctx)); if (!eat(pctx, L_OPTIONS) || !eat_lbrace(pctx)) return (ISC_R_FAILURE); result = dns_c_ndcopts_new(cfgctx->mem, &newopts); if (result != ISC_R_SUCCESS) return (result); result = ISC_R_SUCCESS; while (result == ISC_R_SUCCESS && pctx->currtok != L_RBRACE) { option = pctx->currtok; if (!eat(pctx, pctx->currtok)) return (ISC_R_FAILURE); switch (option) { case L_DEFAULT_SERVER: if (!looking_at_anystring(pctx)) return (result); result = dns_c_ndcopts_setdefserver(newopts, pctx->tokstr); if (result == ISC_R_SUCCESS) result = getnexttoken(pctx); if (result != ISC_R_SUCCESS) return (result); break; case L_DEFAULT_KEY: if (!looking_at_anystring(pctx)) return (result); result = dns_c_ndcopts_setdefkey(newopts, pctx->tokstr); if (result == ISC_R_SUCCESS) result = getnexttoken(pctx); if (result != ISC_R_SUCCESS) return (result); break; default: syntax_error(pctx, pctx->currtok); result = ISC_R_FAILURE; break; } if (result == ISC_R_EXISTS) { parser_warn(pctx, ISC_FALSE, "redefining %s", keyword2str(option)); result = ISC_R_SUCCESS; } else if (result == ISC_R_SUCCESS && !eat_eos(pctx)) result = ISC_R_FAILURE; } if (result == ISC_R_SUCCESS && !eat_rbrace(pctx)) result = ISC_R_FAILURE; if (result == ISC_R_SUCCESS) *opts = newopts; else dns_c_ndcopts_destroy(&newopts); return (result); } static isc_result_t parse_serverstmt(ndcpcontext *pctx, dns_c_ndcserver_t **server) { isc_result_t result = ISC_R_FAILURE; char *servername = NULL; char *keyname = NULL; char *hostname = NULL; dns_c_ndcserver_t *serv = NULL; if (!eat(pctx, L_SERVER)) return (ISC_R_FAILURE); if (!looking_at_anystring(pctx)) return (result); else if (pctx->tokstr[0] == '\0') { parser_error(pctx, ISC_TRUE, "zero-length server name is illegal"); return (ISC_R_FAILURE); } servername = isc_mem_strdup(pctx->themem, pctx->tokstr); if (servername == NULL) { result = ISC_R_FAILURE; goto done; } result = getnexttoken(pctx); if (result != ISC_R_SUCCESS || !eat(pctx, L_LBRACE)) { result = ISC_R_FAILURE; goto done; } while (pctx->currtok != L_RBRACE) { isc_int32_t field = pctx->currtok; if (!eat(pctx, field)) { result = ISC_R_FAILURE; goto done; } switch (field) { case L_KEY: if (!looking_at_anystring(pctx)) { result = ISC_R_FAILURE; goto done; } if (keyname != NULL) { parser_warn(pctx, ISC_FALSE, "multiple 'key' definitions"); isc_mem_free(pctx->themem, keyname); } keyname = isc_mem_strdup(pctx->themem, pctx->tokstr); if (keyname == NULL) result = ISC_R_NOMEMORY; if (result == ISC_R_SUCCESS) result = getnexttoken(pctx); break; case L_HOST: if (!looking_at_anystring(pctx)) { result = ISC_R_FAILURE; goto done; } if (hostname != NULL) { parser_warn(pctx, ISC_FALSE, "multiple 'host' definitions"); isc_mem_free(pctx->themem, hostname); } hostname = isc_mem_strdup(pctx->themem, pctx->tokstr); if (hostname == NULL) result = ISC_R_NOMEMORY; if (result == ISC_R_SUCCESS) result = getnexttoken(pctx); break; default: syntax_error(pctx, field); result = ISC_R_FAILURE; goto done; } if (result != ISC_R_SUCCESS) goto done; if (!eat_eos(pctx)) { result = ISC_R_FAILURE; goto done; } } if (!eat(pctx, L_RBRACE)) { result = ISC_R_FAILURE; goto done; } REQUIRE(servername != NULL); if (keyname == NULL) { parser_error(pctx, ISC_FALSE, "server statement requiresult a key value"); result = ISC_R_FAILURE; goto done; } result = dns_c_ndcserver_new(pctx->themem, &serv); if (result != ISC_R_SUCCESS) goto done; result = dns_c_ndcserver_setname(serv, servername); if (result != ISC_R_SUCCESS) goto done; result = dns_c_ndcserver_setkey(serv, keyname); if (result != ISC_R_SUCCESS) goto done; result = dns_c_ndcserver_sethost(serv, hostname); if (result != ISC_R_SUCCESS) goto done; *server = serv; serv = NULL; done: if (serv != NULL) dns_c_ndcserver_destroy(&serv); if (servername != NULL) isc_mem_free(pctx->themem, servername); if (keyname != NULL) isc_mem_free(pctx->themem, keyname); if (hostname != NULL) isc_mem_free(pctx->themem, hostname); return (result); } static isc_result_t parse_keystmt(ndcpcontext *pctx, dns_c_kdeflist_t *keys) { isc_result_t result = ISC_R_FAILURE; dns_c_ndcctx_t *ctx = pctx->thecontext; dns_c_kdef_t *key = NULL; char *keyname = NULL; char *algorithm = NULL; char *secret = NULL; REQUIRE(DNS_C_NDCCTX_VALID(ctx)); REQUIRE(DNS_C_KDEFLIST_VALID(keys)); if (!eat(pctx, L_KEY)) return (ISC_R_FAILURE); if (!looking_at_anystring(pctx)) return (result); else if (pctx->tokstr[0] == '\0') { parser_error(pctx, ISC_TRUE, "zero length key names are illegal"); return (ISC_R_FAILURE); } keyname = isc_mem_strdup(pctx->themem, pctx->tokstr); result = getnexttoken(pctx); if (result != ISC_R_SUCCESS) goto done; if (!eat(pctx, L_LBRACE)) { result = ISC_R_FAILURE; goto done; } while (pctx->currtok != L_RBRACE) { isc_uint32_t field = pctx->currtok; if (!eat(pctx, field)) { result = ISC_R_FAILURE; goto done; } switch (field) { case L_ALGORITHM: if (!looking_at_anystring(pctx)) { result = ISC_R_FAILURE; goto done; } if (algorithm != NULL) { parser_warn(pctx, ISC_FALSE, "multiple 'algorithm' values"); isc_mem_free(pctx->themem, algorithm); } algorithm = isc_mem_strdup(pctx->themem, pctx->tokstr); if (algorithm == NULL) result = ISC_R_NOMEMORY; if (result == ISC_R_SUCCESS) result = getnexttoken(pctx); break; case L_SECRET: if (!looking_at_anystring(pctx)) { result = ISC_R_FAILURE; goto done; } if (secret != NULL) { parser_warn(pctx, ISC_FALSE, "multiple 'secret' values"); isc_mem_free(pctx->themem, secret); } secret = isc_mem_strdup(pctx->themem, pctx->tokstr); if (secret == NULL) result = ISC_R_NOMEMORY; if (result == ISC_R_SUCCESS) result = getnexttoken(pctx); break; default: syntax_error(pctx, field); result = ISC_R_FAILURE; break; } if (!eat_eos(pctx)) { result = ISC_R_FAILURE; goto done; } } if (!eat(pctx, L_RBRACE)) { result = ISC_R_FAILURE; goto done; } if (algorithm == NULL) { parser_error(pctx, ISC_FALSE, "missing 'algorithm'"); result = ISC_R_FAILURE; } else if (*algorithm == '\0') { parser_error(pctx, ISC_FALSE, "zero length 'algorithm'"); result = ISC_R_FAILURE; } if (secret == NULL) { parser_error(pctx, ISC_FALSE, "missing 'secret'"); result = ISC_R_FAILURE; } else if (*secret == '\0') { parser_error(pctx, ISC_FALSE, "zero length 'secret'"); result = ISC_R_FAILURE; } if (result != ISC_R_SUCCESS) goto done; result = dns_c_kdef_new(keys->mem, keyname, &key); if (result != ISC_R_SUCCESS) goto done; dns_c_kdeflist_append(keys, key, ISC_FALSE); result = dns_c_kdef_setalgorithm(key, algorithm); if (result != ISC_R_SUCCESS) goto done; result = dns_c_kdef_setsecret(key, secret); done: if (keyname != NULL) isc_mem_free(pctx->themem, keyname); if (algorithm != NULL) isc_mem_free(pctx->themem, algorithm); if (secret != NULL) isc_mem_free(pctx->themem, secret); return (result); } static const char * keyword2str(isc_int32_t val) { int i; for (i = 0 ; keyword_tokens[i].token != NULL ; i++) if (keyword_tokens[i].yaccval == val) return (keyword_tokens[i].token); for (i = 0 ; misc_tokens[i].token != NULL ; i++) if (misc_tokens[i].yaccval == val) return (misc_tokens[i].token); return (""); } static isc_boolean_t eat(ndcpcontext *pctx, isc_uint32_t token) { isc_boolean_t rval = ISC_FALSE; if (looking_at(pctx, token)) if (getnexttoken(pctx) == ISC_R_SUCCESS) rval = ISC_TRUE; return (rval); } static isc_boolean_t looking_at(ndcpcontext *pctx, isc_uint32_t token) { isc_boolean_t rval = ISC_TRUE; if (pctx->currtok != token) { parser_error(pctx, ISC_TRUE, "expected a '%s'", keyword2str(token)); rval = ISC_FALSE; } return (rval); } static isc_boolean_t looking_at_anystring(ndcpcontext *pctx) { if (pctx->currtok != L_STRING && pctx->currtok != L_QSTRING) { parser_error(pctx, ISC_TRUE, "expected a string"); return (ISC_FALSE); } return (ISC_TRUE); } static isc_boolean_t eat_lbrace(ndcpcontext *pctx) { return (eat(pctx, L_LBRACE)); } static isc_boolean_t eat_rbrace(ndcpcontext *pctx) { return (eat(pctx, L_RBRACE)); } static isc_boolean_t eat_eos(ndcpcontext *pctx) { return (eat(pctx, L_EOS)); } /* ************************************************** */ /* ************* PRIVATE STUFF ************ */ /* ************************************************** */ static isc_result_t parser_setup(ndcpcontext *pctx, isc_mem_t *mem, const char *filename) { isc_result_t result; isc_lexspecials_t specials; struct keywordtoken *tok; isc_symvalue_t symval; pctx->themem = mem; pctx->thelexer = NULL; pctx->thekeywords = NULL; pctx->thecontext = NULL; pctx->errors = 0; pctx->warnings = 0; pctx->debug_lexer = ISC_TF(getenv("DEBUG_LEXER") != NULL); pctx->prevtok = pctx->currtok = 0; memset(&pctx->prevtokstr[0], 0x0, sizeof pctx->prevtokstr); memset(&pctx->tokstr[0], 0x0, sizeof pctx->tokstr); pctx->intval = 0; memset(&pctx->ip4addr, 0x0, sizeof pctx->ip4addr); memset(&pctx->ip6addr, 0x0, sizeof pctx->ip6addr); result = isc_lex_create(mem, CONF_MAX_IDENT, &pctx->thelexer); if (result != ISC_R_SUCCESS) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG, DNS_LOGMODULE_CONFIG, ISC_LOG_CRITICAL, "%s: Error creating lexer", "dns_c_parse_namedconf"); return (ISC_R_FAILURE); } memset(specials, 0x0, sizeof specials); specials['{'] = 1; specials['}'] = 1; specials[';'] = 1; specials['"'] = 1; isc_lex_setspecials(pctx->thelexer, specials); isc_lex_setcomments(pctx->thelexer, (ISC_LEXCOMMENT_C | ISC_LEXCOMMENT_CPLUSPLUS | ISC_LEXCOMMENT_SHELL)); result = isc_lex_openfile(pctx->thelexer, filename); if (result != ISC_R_SUCCESS) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG, DNS_LOGMODULE_CONFIG, ISC_LOG_CRITICAL, "%s: Error opening file %s", "dns_c_parse_namedconf", filename); return (result); } /* * 97 == bucket size: highest prime < 100 */ result = isc_symtab_create(mem, 97, NULL, NULL, ISC_FALSE, &pctx->thekeywords); if (result != ISC_R_SUCCESS) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG, DNS_LOGMODULE_CONFIG, ISC_LOG_CRITICAL, "%s: Error creating symtab", "dns_c_parse_namedconf"); return (result); } /* * Stick all the keywords into the main symbol table. */ for (tok = &keyword_tokens[0] ; tok->token != NULL ; tok++) { symval.as_integer = tok->yaccval; result = isc_symtab_define(pctx->thekeywords, tok->token, KEYWORD_SYM_TYPE, symval, isc_symexists_reject); if (result != ISC_R_SUCCESS) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG, DNS_LOGMODULE_CONFIG, ISC_LOG_CRITICAL, "%s: Error installing keyword", "dns_c_parse_namedconf"); return (result); } } return (ISC_R_SUCCESS); } static void parser_complain(isc_boolean_t is_warning, isc_boolean_t print_last_token, ndcpcontext *pctx, const char *format, va_list args) { static char where[ISC_DIR_PATHMAX + 100]; static char message[2048]; int level = ISC_LOG_CRITICAL; const char *filename = isc_lex_getsourcename(pctx->thelexer); unsigned long lineno = isc_lex_getsourceline(pctx->thelexer); /* * We can't get a trace of the include files we may be nested in * (lex.c has the structuresult hidden). So we only report the current * file. */ if (filename == NULL) filename = "(none)"; if (is_warning) level = ISC_LOG_WARNING; sprintf(where, "%s:%lu: ", filename, lineno); vsnprintf(message, sizeof(message), format, args); if (print_last_token) { if (dns_lctx != NULL) isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG, DNS_LOGMODULE_CONFIG, level, "%s%s near '%s'", where, message, pctx->tokstr); else fprintf(stderr, "%s%s near '%s'\n", where, message, pctx->tokstr); } else { if (dns_lctx != NULL) isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG, DNS_LOGMODULE_CONFIG, level, "%s%s", where, message); else fprintf(stderr, "%s%s\n", where, message); } } /* * For reporting items that are semantic, but not syntactic errors */ static void parser_error(ndcpcontext *pctx, isc_boolean_t lasttoken, const char *fmt, ...) { va_list args; va_start(args, fmt); parser_complain(ISC_TRUE, lasttoken, pctx, fmt, args); va_end(args); pctx->errors++; } static void parser_warn(ndcpcontext *pctx, isc_boolean_t lasttoken, const char *fmt, ...) { va_list args; va_start(args, fmt); parser_complain(ISC_FALSE, lasttoken, pctx, fmt, args); va_end(args); pctx->warnings++; } /* * Conversion Routines */ static isc_boolean_t is_ip6addr(const char *string, struct in6_addr *addr) { if (inet_pton(AF_INET6, string, addr) != 1) return (ISC_FALSE); return (ISC_TRUE); } static isc_boolean_t is_ip4addr(const char *string, struct in_addr *addr) { char addrbuf[sizeof "xxx.xxx.xxx.xxx" + 1]; const char *p = string; int dots = 0; char dot = '.'; while (*p) { if (!isdigit(*p & 0xff) && *p != dot) return (ISC_FALSE); else if (!isdigit(*p & 0xff)) dots++; p++; } if (dots > 3) return (ISC_FALSE); else if (dots < 3) { if (dots == 1) { if (strlen(string) + 5 <= sizeof (addrbuf)) { strcpy(addrbuf, string); strcat(addrbuf, ".0.0"); } else return (ISC_FALSE); } else if (dots == 2) { if (strlen(string) + 3 <= sizeof (addrbuf)) { strcpy(addrbuf, string); strcat(addrbuf, ".0"); } else return (ISC_FALSE); } } else if (strlen(string) < sizeof addrbuf) strcpy (addrbuf, string); else return (ISC_FALSE); if (inet_pton(AF_INET, addrbuf, addr) != 1) return (ISC_FALSE); return (ISC_TRUE); } static isc_result_t getnexttoken(ndcpcontext *pctx) { isc_token_t token; isc_result_t result; isc_symvalue_t keywordtok; int options = (ISC_LEXOPT_EOF | ISC_LEXOPT_NUMBER | ISC_LEXOPT_QSTRING | ISC_LEXOPT_NOMORE); pctx->prevtok = pctx->currtok; strcpy(pctx->prevtokstr, pctx->tokstr); result = isc_lex_gettoken(pctx->thelexer, options, &token); switch(result) { case ISC_R_SUCCESS: switch (token.type) { case isc_tokentype_unknown: if (pctx->debug_lexer) fprintf(stderr, "unknown token\n"); result = ISC_R_FAILURE; break; case isc_tokentype_special: case isc_tokentype_string: { char *tokstr = &pctx->tokstr[0]; if (token.type == isc_tokentype_special) { tokstr[0] = token.value.as_char; tokstr[1] = '\0'; } else { strncpy(tokstr,token.value.as_textregion.base, CONF_MAX_IDENT); tokstr[CONF_MAX_IDENT - 1] = '\0'; } if (pctx->debug_lexer) fprintf(stderr, "lexer token: %s : %s\n", (token.type == isc_tokentype_special ? "special" : "string"), tokstr); result = isc_symtab_lookup(pctx->thekeywords, tokstr, KEYWORD_SYM_TYPE, &keywordtok); if (result != ISC_R_SUCCESS) { pctx->currtok = L_STRING; if (is_ip4addr(tokstr, &pctx->ip4addr)) pctx->currtok = L_IP4ADDR; else if (is_ip6addr(tokstr, &pctx->ip6addr)) pctx->currtok = L_IP6ADDR; } else pctx->currtok = keywordtok.as_integer; result = ISC_R_SUCCESS; break; } case isc_tokentype_number: pctx->intval = (isc_uint32_t)token.value.as_ulong; pctx->currtok = L_INTEGER; sprintf(pctx->tokstr, "%lu", (unsigned long)pctx->intval); if(pctx->debug_lexer) fprintf(stderr, "lexer token: number : %lu\n", (unsigned long)pctx->intval); break; case isc_tokentype_qstring: strncpy(&pctx->tokstr[0], token.value.as_textregion.base, CONF_MAX_IDENT); pctx->tokstr[CONF_MAX_IDENT - 1] = '\0'; pctx->currtok = L_QSTRING; if (pctx->debug_lexer) fprintf(stderr, "lexer token: qstring : \"%s\"\n", pctx->tokstr); break; case isc_tokentype_eof: result = isc_lex_close(pctx->thelexer); INSIST(result == ISC_R_NOMORE || result == ISC_R_SUCCESS); if (isc_lex_getsourcename(pctx->thelexer) == NULL) { /* * The only way to tell that we closed the * main file and not an included file. */ if (pctx->debug_lexer) fprintf(stderr, "lexer token: EOF\n"); pctx->currtok = L_END_INPUT; } else { if (pctx->debug_lexer) fprintf(stderr, "lexer token: EOF (main)\n"); pctx->currtok = L_END_INCLUDE; } result = ISC_R_SUCCESS; break; case isc_tokentype_initialws: if (pctx->debug_lexer) fprintf(stderr, "lexer token: initial ws\n"); result = ISC_R_FAILURE; break; case isc_tokentype_eol: if (pctx->debug_lexer) fprintf(stderr, "lexer token: eol\n"); result = ISC_R_FAILURE; break; case isc_tokentype_nomore: if (pctx->debug_lexer) fprintf(stderr, "lexer token: nomore\n"); result = ISC_R_FAILURE; break; } break; case ISC_R_EOF: pctx->currtok = 0; result = ISC_R_SUCCESS; break; case ISC_R_UNBALANCED: parser_error(pctx, ISC_TRUE, "unbalanced parentheses"); result = ISC_R_FAILURE; break; case ISC_R_NOSPACE: parser_error(pctx, ISC_TRUE, "token too big"); result = ISC_R_FAILURE; break; case ISC_R_UNEXPECTEDEND: parser_error(pctx, ISC_TRUE, "unexpected EOF"); result = ISC_R_FAILURE; break; default: parser_error(pctx, ISC_TRUE, "unknown lexer error (%d)"); result = ISC_R_FAILURE; break; } return (result); } static void syntax_error(ndcpcontext *pctx, isc_uint32_t keyword) { parser_error(pctx, ISC_FALSE, "syntax error near '%s'", keyword2str(keyword)); }