confndc.c revision 15a44745412679c30a6d022733925af70a38b715
/*
* 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.23 2000/07/27 09:47:07 tale 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 <config.h>
#include <ctype.h>
#include <stdlib.h>
#include <isc/string.h>
#include <isc/dir.h>
#include <isc/lex.h>
#include <isc/mem.h>
#include <isc/net.h>
#include <isc/print.h>
#include <isc/symtab.h>
#include <isc/util.h>
#include <dns/confndc.h>
#include <dns/log.h>
/*
* 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[] = {
{ "<end-of-include>", L_END_INCLUDE },
{ "<end-of-input>", L_END_INPUT },
{ "<ip4 address>", L_IP4ADDR },
{ "<ip6 address>", L_IP6ADDR },
{ "<quoted string>", L_QSTRING },
{ "<quote character>", L_QUOTE },
{ "<string>", 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_boolean_t looking_at_stringoripaddr(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);
}
void
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;
}
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 = NULL;
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_stringoripaddr(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_stringoripaddr(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 requires 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 ("<UNKNOWN KEYWORD VALUE>");
}
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
looking_at_stringoripaddr(ndcpcontext *pctx) {
if (pctx->currtok != L_STRING && pctx->currtok != L_QSTRING &&
pctx->currtok != L_IP4ADDR && pctx->currtok != L_IP6ADDR) {
parser_error(pctx, ISC_TRUE,
"expected a hostname or an ip address");
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_FALSE;
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:
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';
}
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);
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;
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.
*/
pctx->currtok = L_END_INPUT;
} else {
pctx->currtok = L_END_INCLUDE;
}
result = ISC_R_SUCCESS;
break;
case isc_tokentype_initialws:
result = ISC_R_FAILURE;
break;
case isc_tokentype_eol:
result = ISC_R_FAILURE;
break;
case isc_tokentype_nomore:
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));
}