mirror of
https://gitlab.nic.cz/knot/knot-dns.git
synced 2026-05-28 04:02:31 -04:00
conf: improve parsing error detection + add block syntax check support
This commit is contained in:
parent
b75c97cbef
commit
9fbdbe1dc8
13 changed files with 330 additions and 125 deletions
|
|
@ -354,7 +354,7 @@ Default: off
|
|||
.sp
|
||||
Configuration of the server remote control.
|
||||
.sp
|
||||
Caution: The control protocol is not encrypted and is susceptible to replay
|
||||
\fICaution:\fP The control protocol is not encrypted and is susceptible to replay
|
||||
attacks in a short timeframe until message digest expires. For that reason,
|
||||
it is recommended to use default UNIX socket.
|
||||
.INDENT 0.0
|
||||
|
|
@ -381,7 +381,7 @@ Default: \fI\%rundir\fP/knot.sock
|
|||
An ordered list of \fI\%references\fP to ACL rules allowing the remote
|
||||
control.
|
||||
.sp
|
||||
Caution: This option has no effect with UNIX socket.
|
||||
\fICaution:\fP This option has no effect with UNIX socket.
|
||||
.sp
|
||||
Default: empty
|
||||
.SH REMOTE SECTION
|
||||
|
|
@ -603,6 +603,9 @@ If enabled, the server creates zone differences from changes you made to the
|
|||
zone file upon server reload. This option is only relevant if the server
|
||||
is a master server for the zone.
|
||||
.sp
|
||||
\fICaution:\fP This option has no effect with enabled
|
||||
\fI\%dnssec\-signing\fP\&.
|
||||
.sp
|
||||
Default: off
|
||||
.SS max\-journal\-size
|
||||
.sp
|
||||
|
|
@ -613,6 +616,8 @@ Default: unlimited
|
|||
.sp
|
||||
If enabled, automatic DNSSEC signing for the zone is turned on.
|
||||
.sp
|
||||
\fICaution:\fP Cannot be enabled on a slave zone.
|
||||
.sp
|
||||
Default: off
|
||||
.SS kasp\-db
|
||||
.sp
|
||||
|
|
@ -783,7 +788,7 @@ Default: empty
|
|||
.sp
|
||||
A record owner prefix.
|
||||
.sp
|
||||
Caution: \fIprefix\fP doesn’t allow dots, address parts in the synthetic names are
|
||||
\fICaution:\fP \fIprefix\fP doesn’t allow dots, address parts in the synthetic names are
|
||||
separated with a dash.
|
||||
.sp
|
||||
Default: empty
|
||||
|
|
|
|||
|
|
@ -422,7 +422,7 @@ Control section
|
|||
|
||||
Configuration of the server remote control.
|
||||
|
||||
Caution: The control protocol is not encrypted and is susceptible to replay
|
||||
*Caution:* The control protocol is not encrypted and is susceptible to replay
|
||||
attacks in a short timeframe until message digest expires. For that reason,
|
||||
it is recommended to use default UNIX socket.
|
||||
|
||||
|
|
@ -451,7 +451,7 @@ acl
|
|||
An ordered list of :ref:`references<acl_id>` to ACL rules allowing the remote
|
||||
control.
|
||||
|
||||
Caution: This option has no effect with UNIX socket.
|
||||
*Caution:* This option has no effect with UNIX socket.
|
||||
|
||||
Default: empty
|
||||
|
||||
|
|
@ -706,6 +706,9 @@ If enabled, the server creates zone differences from changes you made to the
|
|||
zone file upon server reload. This option is only relevant if the server
|
||||
is a master server for the zone.
|
||||
|
||||
*Caution:* This option has no effect with enabled
|
||||
:ref:`dnssec-signing<zone_dnssec-signing>`.
|
||||
|
||||
Default: off
|
||||
|
||||
.. _zone_max_journal_size:
|
||||
|
|
@ -724,6 +727,8 @@ dnssec-signing
|
|||
|
||||
If enabled, automatic DNSSEC signing for the zone is turned on.
|
||||
|
||||
*Caution:* Cannot be enabled on a slave zone.
|
||||
|
||||
Default: off
|
||||
|
||||
.. _zone_kasp_db:
|
||||
|
|
@ -923,7 +928,7 @@ prefix
|
|||
|
||||
A record owner prefix.
|
||||
|
||||
Caution: *prefix* doesn’t allow dots, address parts in the synthetic names are
|
||||
*Caution:* *prefix* doesn’t allow dots, address parts in the synthetic names are
|
||||
separated with a dash.
|
||||
|
||||
Default: empty
|
||||
|
|
|
|||
|
|
@ -30,6 +30,12 @@
|
|||
#include "libknot/internal/namedb/namedb_lmdb.h"
|
||||
#include "libknot/internal/sockaddr.h"
|
||||
#include "libknot/yparser/ypformat.h"
|
||||
#include "libknot/yparser/yptrafo.h"
|
||||
|
||||
/*! Configuration specific logging. */
|
||||
#define CONF_LOG(severity, msg, ...) do { \
|
||||
log_msg(severity, "config, " msg, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define MAX_INCLUDE_DEPTH 5
|
||||
|
||||
|
|
@ -44,7 +50,7 @@ static void rm_dir(const char *path)
|
|||
{
|
||||
DIR *dir = opendir(path);
|
||||
if (dir == NULL) {
|
||||
log_warning("failed to remove directory '%s'", path);
|
||||
CONF_LOG(LOG_WARNING, "failed to remove directory '%s'", path);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +60,7 @@ static void rm_dir(const char *path)
|
|||
|
||||
struct dirent *entry = malloc(len);
|
||||
if (entry == NULL) {
|
||||
log_warning("failed to remove directory '%s'", path);
|
||||
CONF_LOG(LOG_WARNING, "failed to remove directory '%s'", path);
|
||||
closedir(dir);
|
||||
return;
|
||||
}
|
||||
|
|
@ -83,7 +89,7 @@ static void rm_dir(const char *path)
|
|||
|
||||
// Secondly, delete the directory if it is empty.
|
||||
if (ret != 0 || remove(path) != 0) {
|
||||
log_warning("failed to remove whole directory '%s'", path);
|
||||
CONF_LOG(LOG_WARNING, "failed to remove whole directory '%s'", path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +128,7 @@ int conf_new(
|
|||
char tpl[] = "/tmp/knot-confdb.XXXXXX";
|
||||
lmdb_opts.path = mkdtemp(tpl);
|
||||
if (lmdb_opts.path == NULL) {
|
||||
log_error("failed to create temporary directory");
|
||||
CONF_LOG(LOG_ERR, "failed to create temporary directory");
|
||||
ret = KNOT_ENOMEM;
|
||||
goto new_error;
|
||||
}
|
||||
|
|
@ -370,42 +376,139 @@ void conf_deactivate_modules(
|
|||
query_plan_free(query_plan);
|
||||
}
|
||||
|
||||
static int parser_process(
|
||||
static int exec_callbacks(
|
||||
const yp_item_t *item,
|
||||
conf_check_t *args)
|
||||
{
|
||||
for (size_t i = 0; i < YP_MAX_MISC_COUNT; i++) {
|
||||
conf_check_f *fcn = (conf_check_f *)item->misc[i];
|
||||
if (fcn == NULL) {
|
||||
break;
|
||||
}
|
||||
int ret;
|
||||
if ((ret = fcn(args)) != KNOT_EOK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return KNOT_EOK;
|
||||
}
|
||||
|
||||
static int previous_block_calls(
|
||||
conf_t *conf,
|
||||
namedb_txn_t *txn,
|
||||
conf_previous_t *prev,
|
||||
const char **err_str)
|
||||
{
|
||||
if (prev->id_len > 0) {
|
||||
assert(prev->key0 != NULL);
|
||||
|
||||
conf_check_t args = {
|
||||
.conf = conf,
|
||||
.txn = txn,
|
||||
.previous = prev,
|
||||
.err_str = err_str
|
||||
};
|
||||
|
||||
// Execute previous block callbacks.
|
||||
int ret = exec_callbacks(prev->key0, &args);
|
||||
if (ret != KNOT_EOK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return KNOT_EOK;
|
||||
}
|
||||
|
||||
static int item_calls(
|
||||
conf_t *conf,
|
||||
namedb_txn_t *txn,
|
||||
yp_parser_t *parser,
|
||||
yp_check_ctx_t *ctx,
|
||||
size_t *incl_depth)
|
||||
conf_previous_t *prev,
|
||||
size_t *incl_depth,
|
||||
const char **err_str)
|
||||
{
|
||||
int ret = yp_scheme_check_parser(ctx, parser);
|
||||
if (ret != KNOT_EOK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = conf_db_set(conf, txn, ctx);
|
||||
if (ret != KNOT_EOK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
const yp_item_t *item = (ctx->event == YP_EKEY0) ? ctx->key0 : ctx->key1;
|
||||
conf_call_f *sem_check = (conf_call_f *)item->misc[0];
|
||||
conf_call_f *callback = (conf_call_f *)item->misc[1];
|
||||
conf_args_t args = {
|
||||
conf, txn, parser->file.name, incl_depth, ctx->key0, ctx->key1,
|
||||
ctx->id, ctx->id_len, ctx->data, ctx->data_len
|
||||
conf_check_t args = {
|
||||
.conf = conf,
|
||||
.txn = txn,
|
||||
.parser = parser,
|
||||
.check = ctx,
|
||||
.include_depth = incl_depth,
|
||||
.previous = prev,
|
||||
.err_str = err_str
|
||||
};
|
||||
|
||||
// Call semantic check if any.
|
||||
if (sem_check != NULL && (ret = sem_check(&args)) != KNOT_EOK) {
|
||||
return ret;
|
||||
const yp_item_t *item;
|
||||
|
||||
// Prepare previous context.
|
||||
switch (ctx->event) {
|
||||
case YP_EKEY0:
|
||||
// Reset previous context id.
|
||||
prev->id_len = 0;
|
||||
// Set previous context key0 if group item.
|
||||
if (ctx->key0->type == YP_TGRP) {
|
||||
prev->key0 = ctx->key0;
|
||||
return KNOT_EOK;
|
||||
}
|
||||
item = ctx->key0;
|
||||
break;
|
||||
case YP_EID:
|
||||
memcpy(prev->id, ctx->id, ctx->id_len);
|
||||
prev->id_len = ctx->id_len;
|
||||
prev->file = parser->file.name;
|
||||
prev->line = parser->line_count;
|
||||
item = ctx->key1;
|
||||
break;
|
||||
default:
|
||||
assert(ctx->event == YP_EKEY1);
|
||||
item = ctx->key1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Call callback function if any.
|
||||
if (callback != NULL && (ret = callback(&args)) != KNOT_EOK) {
|
||||
return ret;
|
||||
// Execute item callbacks.
|
||||
return exec_callbacks(item, &args);
|
||||
}
|
||||
|
||||
#define CONF_LOG_LINE(input, is_file, line, msg, ...) do { \
|
||||
CONF_LOG(LOG_ERR, "%s%s%sline %zu, " msg, \
|
||||
(is_file ? "file '" : ""), (is_file ? input : ""), \
|
||||
(is_file ? "', " : ""), line, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static void log_current_err(
|
||||
const char *input,
|
||||
bool is_file,
|
||||
yp_parser_t *parser,
|
||||
int ret,
|
||||
const char *err_str)
|
||||
{
|
||||
CONF_LOG_LINE(input, is_file, parser->line_count,
|
||||
"item '%.*s', value '%.*s' (%s)",
|
||||
(int)parser->key_len, parser->key,
|
||||
(int)parser->data_len, parser->data,
|
||||
(err_str != NULL ? err_str : knot_strerror(ret)));
|
||||
}
|
||||
|
||||
static void log_prev_err(
|
||||
const char *input,
|
||||
bool is_file,
|
||||
struct conf_previous *prev,
|
||||
int ret,
|
||||
const char *err_str)
|
||||
{
|
||||
char buff[512];
|
||||
size_t len = sizeof(buff);
|
||||
|
||||
// Get textual previous identifier.
|
||||
if (yp_item_to_txt(prev->key0->var.g.id, prev->id, prev->id_len,
|
||||
buff, &len, YP_SNOQUOTE) != KNOT_EOK) {
|
||||
buff[0] = '\0';
|
||||
}
|
||||
|
||||
return KNOT_EOK;
|
||||
CONF_LOG_LINE(input, is_file, prev->line, "%s '%s' (%s)",
|
||||
prev->key0->name + 1, buff,
|
||||
(err_str != NULL ? err_str : knot_strerror(ret)));
|
||||
}
|
||||
|
||||
int conf_parse(
|
||||
|
|
@ -413,15 +516,17 @@ int conf_parse(
|
|||
namedb_txn_t *txn,
|
||||
const char *input,
|
||||
bool is_file,
|
||||
size_t *incl_depth)
|
||||
size_t *incl_depth,
|
||||
struct conf_previous *prev)
|
||||
{
|
||||
if (conf == NULL || txn == NULL || input == NULL ||
|
||||
incl_depth == NULL) {
|
||||
incl_depth == NULL || prev == NULL) {
|
||||
return KNOT_EINVAL;
|
||||
}
|
||||
|
||||
// Check for include loop.
|
||||
if ((*incl_depth)++ > MAX_INCLUDE_DEPTH) {
|
||||
CONF_LOG(LOG_ERR, "include loop detected");
|
||||
return KNOT_EPARSEFAIL;
|
||||
}
|
||||
|
||||
|
|
@ -432,43 +537,71 @@ int conf_parse(
|
|||
yp_init(parser);
|
||||
|
||||
int ret;
|
||||
|
||||
// Set parser source.
|
||||
if (is_file) {
|
||||
ret = yp_set_input_file(parser, input);
|
||||
} else {
|
||||
ret = yp_set_input_string(parser, input, strlen(input));
|
||||
}
|
||||
if (ret != KNOT_EOK) {
|
||||
goto init_error;
|
||||
CONF_LOG(LOG_ERR, "failed to load file '%s' (%s)",
|
||||
input, knot_strerror(ret));
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
// Initialize parser check context.
|
||||
yp_check_ctx_t *ctx = yp_scheme_check_init(conf->scheme);
|
||||
if (ctx == NULL) {
|
||||
ret = KNOT_ENOMEM;
|
||||
goto init_error;
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
int check_ret = KNOT_EOK;
|
||||
const char *err_str = NULL;
|
||||
|
||||
// Parse the configuration.
|
||||
while ((ret = yp_parse(parser)) == KNOT_EOK) {
|
||||
ret = parser_process(conf, txn, parser, ctx, incl_depth);
|
||||
if (ret != KNOT_EOK) {
|
||||
check_ret = yp_scheme_check_parser(ctx, parser);
|
||||
if (check_ret != KNOT_EOK) {
|
||||
log_current_err(input, is_file, parser, check_ret, NULL);
|
||||
break;
|
||||
}
|
||||
check_ret = conf_db_set(conf, txn, ctx);
|
||||
if (check_ret != KNOT_EOK) {
|
||||
log_current_err(input, is_file, parser, check_ret, NULL);
|
||||
break;
|
||||
}
|
||||
if (ctx->event != YP_EKEY1) {
|
||||
check_ret = previous_block_calls(conf, txn, prev, &err_str);
|
||||
if (check_ret != KNOT_EOK) {
|
||||
log_prev_err(input, is_file, prev, check_ret, err_str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
check_ret = item_calls(conf, txn, parser, ctx, prev, incl_depth,
|
||||
&err_str);
|
||||
if (check_ret != KNOT_EOK) {
|
||||
log_current_err(input, is_file, parser, check_ret, err_str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
yp_scheme_check_deinit(ctx);
|
||||
|
||||
if (ret != KNOT_EOF) {
|
||||
log_error("invalid configuration%s%s%s, line %zu (%s)",
|
||||
(is_file ? " file '" : ""),
|
||||
(is_file ? input : ""),
|
||||
(is_file ? "'" : ""),
|
||||
parser->line_count, knot_strerror(ret));
|
||||
goto init_error;
|
||||
if (ret == KNOT_EOF) {
|
||||
// Call the last block callbacks.
|
||||
ret = previous_block_calls(conf, txn, prev, &err_str);
|
||||
if (ret != KNOT_EOK) {
|
||||
log_prev_err(input, is_file, prev, ret, err_str);
|
||||
}
|
||||
} else if (ret != KNOT_EOK) {
|
||||
log_current_err(input, is_file, parser, ret, NULL);
|
||||
} else {
|
||||
ret = check_ret;
|
||||
}
|
||||
|
||||
yp_scheme_check_deinit(ctx);
|
||||
parse_error:
|
||||
(*incl_depth)--;
|
||||
|
||||
ret = KNOT_EOK;
|
||||
init_error:
|
||||
yp_deinit(parser);
|
||||
free(parser);
|
||||
|
||||
|
|
@ -505,9 +638,10 @@ int conf_import(
|
|||
}
|
||||
|
||||
size_t depth = 0;
|
||||
conf_previous_t prev = { NULL };
|
||||
|
||||
// Parse and import given file.
|
||||
ret = conf_parse(conf, &txn, input, is_file, &depth);
|
||||
ret = conf_parse(conf, &txn, input, is_file, &depth, &prev);
|
||||
if (ret != KNOT_EOK) {
|
||||
conf->api->txn_abort(&txn);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ typedef struct {
|
|||
struct query_plan *query_plan;
|
||||
} conf_t;
|
||||
|
||||
struct conf_previous;
|
||||
|
||||
/*!
|
||||
* Returns the active configuration.
|
||||
*/
|
||||
|
|
@ -157,13 +159,14 @@ void conf_deactivate_modules(
|
|||
/*!
|
||||
* Parses textual configuration from the string or from the file.
|
||||
*
|
||||
* This function is used for includes processing!
|
||||
* This function is not for direct using, just for includes processing!
|
||||
*
|
||||
* \param[in] conf Configuration.
|
||||
* \param[in] txn Transaction.
|
||||
* \param[in] input Configuration string or filename.
|
||||
* \param[in] is_file Specifies if the input is string or input filename.
|
||||
* \param[in] incl_depth The current include depth counter.
|
||||
* \param[in] prev Previous context.
|
||||
*
|
||||
* \return Error code, KNOT_EOK if success.
|
||||
*/
|
||||
|
|
@ -172,7 +175,8 @@ int conf_parse(
|
|||
namedb_txn_t *txn,
|
||||
const char *input,
|
||||
bool is_file,
|
||||
size_t *incl_depth
|
||||
size_t *incl_depth,
|
||||
struct conf_previous *prev
|
||||
);
|
||||
|
||||
/*!
|
||||
|
|
|
|||
|
|
@ -29,6 +29,15 @@
|
|||
#include "libknot/internal/strlcat.h"
|
||||
#include "libknot/yparser/yptrafo.h"
|
||||
|
||||
/*! Configuration specific logging. */
|
||||
#define CONF_LOG(severity, msg, ...) do { \
|
||||
log_msg(severity, "config, " msg, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define CONF_LOG_ZONE(severity, zone, msg, ...) do { \
|
||||
log_msg_zone(severity, zone, "config, " msg, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static conf_val_t raw_id_get(
|
||||
conf_t *conf,
|
||||
namedb_txn_t *txn,
|
||||
|
|
@ -42,8 +51,8 @@ static conf_val_t raw_id_get(
|
|||
val.code = conf_db_get(conf, txn, key0_name, key1_name, id, id_len, &val);
|
||||
switch (val.code) {
|
||||
default:
|
||||
log_error("failed to read configuration '%s/%s' (%s)",
|
||||
key0_name + 1, key1_name + 1, knot_strerror(val.code));
|
||||
CONF_LOG(LOG_ERR, "failed to read '%s/%s' (%s)",
|
||||
key0_name + 1, key1_name + 1, knot_strerror(val.code));
|
||||
// FALLTHROUGH
|
||||
case KNOT_EOK:
|
||||
case KNOT_ENOENT:
|
||||
|
|
@ -59,7 +68,7 @@ conf_val_t conf_get_txn(
|
|||
{
|
||||
// Check for empty key1.
|
||||
if (key1_name == NULL) {
|
||||
log_error("incomplete configuration specification");
|
||||
CONF_LOG(LOG_ERR, "missing parameters");
|
||||
conf_val_t val = { NULL };
|
||||
val.code = KNOT_EINVAL;
|
||||
return val;
|
||||
|
|
@ -84,7 +93,7 @@ conf_val_t conf_id_get_txn(
|
|||
}
|
||||
conf_db_val(id);
|
||||
} else {
|
||||
log_error("incomplete configuration specification");
|
||||
CONF_LOG(LOG_ERR, "missing parameters");
|
||||
conf_val_t val = { NULL };
|
||||
val.code = KNOT_EINVAL;
|
||||
return val;
|
||||
|
|
@ -101,7 +110,7 @@ conf_val_t conf_mod_get_txn(
|
|||
{
|
||||
// Check for empty input.
|
||||
if (key1_name == NULL || mod_id == NULL) {
|
||||
log_error("incomplete configuration specification");
|
||||
CONF_LOG(LOG_ERR, "missing parameters");
|
||||
conf_val_t val = { NULL };
|
||||
val.code = KNOT_EINVAL;
|
||||
return val;
|
||||
|
|
@ -119,7 +128,7 @@ conf_val_t conf_zone_get_txn(
|
|||
conf_val_t val = { NULL };
|
||||
|
||||
if (dname == NULL) {
|
||||
log_error("incomplete configuration specification");
|
||||
CONF_LOG(LOG_ERR, "missing parameters");
|
||||
val.code = KNOT_EINVAL;
|
||||
return val;
|
||||
}
|
||||
|
|
@ -132,8 +141,8 @@ conf_val_t conf_zone_get_txn(
|
|||
case KNOT_EOK:
|
||||
return val;
|
||||
default:
|
||||
log_zone_error(dname, "failed to read configuration '%s/%s' (%s)",
|
||||
C_ZONE + 1, key1_name + 1, knot_strerror(val.code));
|
||||
CONF_LOG_ZONE(LOG_ERR, dname, "failed to read '%s/%s' (%s)",
|
||||
C_ZONE + 1, key1_name + 1, knot_strerror(val.code));
|
||||
// FALLTHROUGH
|
||||
case KNOT_ENOENT:
|
||||
break;
|
||||
|
|
@ -149,8 +158,8 @@ conf_val_t conf_zone_get_txn(
|
|||
val.data, val.len, &val);
|
||||
break;
|
||||
default:
|
||||
log_zone_error(dname, "failed to read configuration '%s/%s' (%s)",
|
||||
C_ZONE + 1, C_TPL + 1, knot_strerror(val.code));
|
||||
CONF_LOG_ZONE(LOG_ERR, dname, "failed to read '%s/%s' (%s)",
|
||||
C_ZONE + 1, C_TPL + 1, knot_strerror(val.code));
|
||||
// FALLTHROUGH
|
||||
case KNOT_ENOENT:
|
||||
// Use the default template.
|
||||
|
|
@ -160,8 +169,8 @@ conf_val_t conf_zone_get_txn(
|
|||
|
||||
switch (val.code) {
|
||||
default:
|
||||
log_zone_error(dname, "failed to read configuration '%s/%s' (%s)",
|
||||
C_TPL + 1, key1_name + 1, knot_strerror(val.code));
|
||||
CONF_LOG_ZONE(LOG_ERR, dname, "failed to read '%s/%s' (%s)",
|
||||
C_TPL + 1, key1_name + 1, knot_strerror(val.code));
|
||||
// FALLTHROUGH
|
||||
case KNOT_EOK:
|
||||
case KNOT_ENOENT:
|
||||
|
|
@ -182,8 +191,8 @@ conf_val_t conf_default_get_txn(
|
|||
CONF_DEFAULT_ID + 1, CONF_DEFAULT_ID[0], &val);
|
||||
switch (val.code) {
|
||||
default:
|
||||
log_error("failed to read configuration '%s/%s' (%s)",
|
||||
C_TPL + 1, key1_name + 1, knot_strerror(val.code));
|
||||
CONF_LOG(LOG_ERR, "failed to read '%s/%s' (%s)",
|
||||
C_TPL + 1, key1_name + 1, knot_strerror(val.code));
|
||||
// FALLTHROUGH
|
||||
case KNOT_EOK:
|
||||
case KNOT_ENOENT:
|
||||
|
|
@ -206,8 +215,8 @@ size_t conf_id_count_txn(
|
|||
case KNOT_EOK:
|
||||
break;
|
||||
default:
|
||||
log_error("failed to iterate through configuration '%s' (%s)",
|
||||
key0_name + 1, knot_strerror(ret));
|
||||
CONF_LOG(LOG_ERR, "failed to iterate through '%s' (%s)",
|
||||
key0_name + 1, knot_strerror(ret));
|
||||
// FALLTHROUGH
|
||||
case KNOT_ENOENT:
|
||||
return count;
|
||||
|
|
@ -232,7 +241,7 @@ conf_iter_t conf_iter_txn(
|
|||
iter.code = conf_db_iter_begin(conf, txn, key0_name, &iter);
|
||||
switch (iter.code) {
|
||||
default:
|
||||
log_error("failed to iterate thgrough configuration '%s' (%s)",
|
||||
CONF_LOG(LOG_ERR, "failed to iterate thgrough '%s' (%s)",
|
||||
key0_name + 1, knot_strerror(iter.code));
|
||||
// FALLTHROUGH
|
||||
case KNOT_EOK:
|
||||
|
|
@ -248,7 +257,7 @@ void conf_iter_next(
|
|||
iter->code = conf_db_iter_next(conf, iter);
|
||||
switch (iter->code) {
|
||||
default:
|
||||
log_error("failed to read next configuration item (%s)",
|
||||
CONF_LOG(LOG_ERR, "failed to read next item (%s)",
|
||||
knot_strerror(iter->code));
|
||||
// FALLTHROUGH
|
||||
case KNOT_EOK:
|
||||
|
|
@ -267,7 +276,7 @@ conf_val_t conf_iter_id(
|
|||
&val.blob_len);
|
||||
switch (val.code) {
|
||||
default:
|
||||
log_error("failed to read configuration identifier (%s)",
|
||||
CONF_LOG(LOG_ERR, "failed to read identifier (%s)",
|
||||
knot_strerror(val.code));
|
||||
// FALLTHROUGH
|
||||
case KNOT_EOK:
|
||||
|
|
@ -620,12 +629,12 @@ static char* get_filename(
|
|||
}
|
||||
break;
|
||||
case '\0':
|
||||
log_zone_warning(zone, "ignoring missing trailing "
|
||||
"zonefile formatter");
|
||||
CONF_LOG_ZONE(LOG_WARNING, zone, "ignoring missing "
|
||||
"trailing zonefile formatter");
|
||||
continue;
|
||||
default:
|
||||
log_zone_warning(zone, "ignoring zonefile formatter '%%%c'",
|
||||
type);
|
||||
CONF_LOG_ZONE(LOG_WARNING, zone, "ignoring zonefile "
|
||||
"formatter '%%%c'", type);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -742,7 +751,8 @@ int conf_user_txn(
|
|||
if (grp != NULL) {
|
||||
*gid = grp->gr_gid;
|
||||
} else {
|
||||
log_error("invalid group name '%s'", sep_pos + 1);
|
||||
CONF_LOG(LOG_ERR, "invalid group name '%s'",
|
||||
sep_pos + 1);
|
||||
free(user);
|
||||
return KNOT_EINVAL;
|
||||
}
|
||||
|
|
@ -758,7 +768,7 @@ int conf_user_txn(
|
|||
if (pwd != NULL) {
|
||||
*uid = pwd->pw_uid;
|
||||
} else {
|
||||
log_error("invalid user name '%s'", user);
|
||||
CONF_LOG(LOG_ERR, "invalid user name '%s'", user);
|
||||
free(user);
|
||||
return KNOT_EINVAL;
|
||||
}
|
||||
|
|
@ -792,7 +802,7 @@ conf_remote_t conf_remote_txn(
|
|||
// Get remote address.
|
||||
conf_val_t val = conf_id_get_txn(conf, txn, C_RMT, C_ADDR, id);
|
||||
if (val.code != KNOT_EOK) {
|
||||
log_error("invalid remote in configuration");
|
||||
CONF_LOG(LOG_ERR, "invalid remote");
|
||||
free(rundir);
|
||||
return out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ const yp_item_t conf_scheme[] = {
|
|||
#endif
|
||||
/***********/
|
||||
{ C_TPL, YP_TGRP, YP_VGRP = { desc_template }, YP_FMULTI },
|
||||
{ C_ZONE, YP_TGRP, YP_VGRP = { desc_zone }, YP_FMULTI },
|
||||
{ C_INCL, YP_TSTR, YP_VNONE, YP_FNONE, { NULL, include_file } },
|
||||
{ C_ZONE, YP_TGRP, YP_VGRP = { desc_zone }, YP_FMULTI, { check_zone } },
|
||||
{ C_INCL, YP_TSTR, YP_VNONE, YP_FNONE, { include_file } },
|
||||
{ NULL }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -180,29 +180,62 @@ int mod_id_to_txt(
|
|||
}
|
||||
|
||||
int check_ref(
|
||||
conf_args_t *args)
|
||||
conf_check_t *args)
|
||||
{
|
||||
const yp_item_t *parent = args->key1->var.r.ref;
|
||||
const char *err_str = "invalid reference";
|
||||
|
||||
const yp_item_t *parent = args->check->key1->var.r.ref;
|
||||
|
||||
// Try to find the id in the referenced category.
|
||||
return conf_db_get(args->conf, args->txn, parent->name, NULL,
|
||||
args->data, args->data_len, NULL);
|
||||
int ret = conf_db_get(args->conf, args->txn, parent->name, NULL,
|
||||
args->check->data, args->check->data_len, NULL);
|
||||
if (ret != KNOT_EOK) {
|
||||
*args->err_str = err_str;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int check_modref(
|
||||
conf_args_t *args)
|
||||
conf_check_t *args)
|
||||
{
|
||||
const yp_name_t *mod_name = (const yp_name_t *)args->data;
|
||||
const uint8_t *id = args->data + 1 + args->data[0];
|
||||
size_t id_len = args->data_len - 1 - args->data[0];
|
||||
const char *err_str = "invalid module reference";
|
||||
|
||||
const yp_name_t *mod_name = (const yp_name_t *)args->check->data;
|
||||
const uint8_t *id = args->check->data + 1 + args->check->data[0];
|
||||
size_t id_len = args->check->data_len - 1 - args->check->data[0];
|
||||
|
||||
// Try to find the module with id.
|
||||
return conf_db_get(args->conf, args->txn, mod_name, NULL, id, id_len,
|
||||
NULL);
|
||||
int ret = conf_db_get(args->conf, args->txn, mod_name, NULL, id, id_len,
|
||||
NULL);
|
||||
if (ret != KNOT_EOK) {
|
||||
*args->err_str = err_str;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int check_zone(
|
||||
conf_check_t *args)
|
||||
{
|
||||
const char *err_str = "slave zone with DNSSEC signing";
|
||||
|
||||
conf_val_t master = conf_zone_get_txn(args->conf, args->txn,
|
||||
C_MASTER, args->previous->id);
|
||||
conf_val_t dnssec = conf_zone_get_txn(args->conf, args->txn,
|
||||
C_DNSSEC_SIGNING, args->previous->id);
|
||||
|
||||
// DNSSEC signing is not possible with slave zone.
|
||||
if (conf_val_count(&master) > 0 && conf_bool(&dnssec)) {
|
||||
*args->err_str = err_str;
|
||||
return KNOT_EINVAL;
|
||||
}
|
||||
|
||||
return KNOT_EOK;
|
||||
}
|
||||
|
||||
int include_file(
|
||||
conf_args_t *args)
|
||||
conf_check_t *args)
|
||||
{
|
||||
size_t max_path = 4096;
|
||||
char *path = malloc(max_path);
|
||||
|
|
@ -212,12 +245,13 @@ int include_file(
|
|||
|
||||
// Prepare absolute include path.
|
||||
int ret;
|
||||
if (args->data[0] == '/') {
|
||||
if (args->check->data[0] == '/') {
|
||||
ret = snprintf(path, max_path, "%.*s",
|
||||
(int)args->data_len, args->data);
|
||||
(int)args->check->data_len, args->check->data);
|
||||
} else {
|
||||
char *full_current_name = realpath((args->file_name != NULL) ?
|
||||
args->file_name : "./", NULL);
|
||||
const char *file_name = args->parser->file.name != NULL ?
|
||||
args->parser->file.name : "./";
|
||||
char *full_current_name = realpath(file_name, NULL);
|
||||
if (full_current_name == NULL) {
|
||||
free(path);
|
||||
return KNOT_ENOMEM;
|
||||
|
|
@ -225,7 +259,7 @@ int include_file(
|
|||
|
||||
ret = snprintf(path, max_path, "%s/%.*s",
|
||||
dirname(full_current_name),
|
||||
(int)args->data_len, args->data);
|
||||
(int)args->check->data_len, args->check->data);
|
||||
free(full_current_name);
|
||||
}
|
||||
if (ret <= 0 || ret >= max_path) {
|
||||
|
|
@ -244,7 +278,7 @@ int include_file(
|
|||
// Process regular file.
|
||||
if (S_ISREG(file_stat.st_mode)) {
|
||||
ret = conf_parse(args->conf, args->txn, path, true,
|
||||
args->incl_depth);
|
||||
args->include_depth, args->previous);
|
||||
free(path);
|
||||
return ret;
|
||||
} else if (!S_ISDIR(file_stat.st_mode)) {
|
||||
|
|
@ -295,7 +329,7 @@ int include_file(
|
|||
}
|
||||
|
||||
ret = conf_parse(args->conf, args->txn, path, true,
|
||||
args->incl_depth);
|
||||
args->include_depth, args->previous);
|
||||
if (ret != KNOT_EOK) {
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,20 +31,25 @@
|
|||
#include "knot/conf/conf.h"
|
||||
#include "libknot/yparser/ypscheme.h"
|
||||
|
||||
typedef struct conf_previous {
|
||||
const yp_item_t *key0;
|
||||
size_t id_len;
|
||||
uint8_t id[YP_MAX_ID_LEN];
|
||||
const char *file;
|
||||
size_t line;
|
||||
} conf_previous_t;
|
||||
|
||||
typedef struct {
|
||||
conf_t *conf;
|
||||
namedb_txn_t *txn;
|
||||
const char *file_name; // Current config file.
|
||||
size_t *incl_depth; // Current include depth.
|
||||
const yp_item_t *key0;
|
||||
const yp_item_t *key1;
|
||||
const uint8_t *id;
|
||||
size_t id_len;
|
||||
const uint8_t *data;
|
||||
size_t data_len;
|
||||
} conf_args_t;
|
||||
const yp_parser_t *parser;
|
||||
const yp_check_ctx_t *check;
|
||||
size_t *include_depth;
|
||||
conf_previous_t *previous;
|
||||
const char **err_str;
|
||||
} conf_check_t;
|
||||
|
||||
typedef int conf_call_f(conf_args_t *);
|
||||
typedef int conf_check_f(conf_check_t *);
|
||||
|
||||
int hex_text_to_bin(
|
||||
char const *txt,
|
||||
|
|
@ -75,13 +80,17 @@ int mod_id_to_txt(
|
|||
);
|
||||
|
||||
int check_ref(
|
||||
conf_args_t *args
|
||||
conf_check_t *args
|
||||
);
|
||||
|
||||
int check_modref(
|
||||
conf_args_t *args
|
||||
conf_check_t *args
|
||||
);
|
||||
|
||||
int check_zone(
|
||||
conf_check_t *args
|
||||
);
|
||||
|
||||
int include_file(
|
||||
conf_args_t *args
|
||||
conf_check_t *args
|
||||
);
|
||||
|
|
|
|||
|
|
@ -43,12 +43,12 @@ static const lookup_table_t synthetic_types[] = {
|
|||
};
|
||||
|
||||
static int check_origin(
|
||||
conf_args_t *args)
|
||||
conf_check_t *args)
|
||||
{
|
||||
conf_val_t val = { NULL };
|
||||
|
||||
val.code = conf_db_get(args->conf, args->txn, C_MOD_SYNTH_RECORD,
|
||||
MOD_TYPE, args->id, args->id_len, &val);
|
||||
MOD_TYPE, args->check->id, args->check->id_len, &val);
|
||||
if (val.code != KNOT_EOK) {
|
||||
return val.code;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ static const struct error errors[] = {
|
|||
|
||||
/* Yparser errors. */
|
||||
{ KNOT_YP_EINVAL_ITEM, "invalid item" },
|
||||
{ KNOT_YP_EINVAL_ID, "invalid identifier" },
|
||||
{ KNOT_YP_EINVAL_DATA, "invalid value" },
|
||||
{ KNOT_YP_EINVAL_INDENT, "invalid indentation" },
|
||||
{ KNOT_YP_ENOTSUP_DATA, "value not supported" },
|
||||
|
|
@ -131,7 +132,7 @@ static const struct error errors[] = {
|
|||
/* Configuration errors. */
|
||||
{ KNOT_CONF_EMPTY, "empty configuration database" },
|
||||
{ KNOT_CONF_EVERSION, "invalid configuration database version" },
|
||||
{ KNOT_CONF_EREDEFINE, "identifier already specified" },
|
||||
{ KNOT_CONF_EREDEFINE, "duplicate identifier" },
|
||||
|
||||
/* Processing errors. */
|
||||
{ KNOT_LAYER_ERROR, "processing layer error" },
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ enum knot_error {
|
|||
|
||||
/* Yparser errors. */
|
||||
KNOT_YP_EINVAL_ITEM,
|
||||
KNOT_YP_EINVAL_ID,
|
||||
KNOT_YP_EINVAL_DATA,
|
||||
KNOT_YP_EINVAL_INDENT,
|
||||
KNOT_YP_ENOTSUP_DATA,
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@ static int check_key1(
|
|||
if (ctx->key0->var.g.id != NULL) {
|
||||
// Check if key1 is not id item.
|
||||
if (key1 == ctx->key0->var.g.id) {
|
||||
return KNOT_YP_EINVAL_ITEM;
|
||||
return KNOT_YP_EINVAL_ID;
|
||||
}
|
||||
|
||||
if (ctx->id_len == 0) {
|
||||
|
|
@ -338,7 +338,7 @@ static int check_id(
|
|||
|
||||
// Id item must be the first one.
|
||||
if (id != ctx->key0->var.g.id) {
|
||||
return KNOT_YP_EINVAL_ITEM;
|
||||
return KNOT_YP_EINVAL_ID;
|
||||
}
|
||||
|
||||
// Textual id must not be empty.
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@
|
|||
#define YP_MAX_DATA_LEN 32768
|
||||
/*! Integer item nil definition. */
|
||||
#define YP_NIL INT64_MIN
|
||||
/*! Maximal number of miscellaneous callbacks/pointers. */
|
||||
#define YP_MAX_MISC_COUNT 4
|
||||
|
||||
/*! Helper macros for item variables definition. */
|
||||
#define YP_VNONE .var.i = { 0 }
|
||||
|
|
@ -163,7 +165,7 @@ struct yp_item {
|
|||
/*! Item flags. */
|
||||
yp_flag_t flags;
|
||||
/*! Arbitrary data/callbacks. */
|
||||
const void *misc[3];
|
||||
const void *misc[YP_MAX_MISC_COUNT];
|
||||
/*! Item group subitems (name=NULL terminated array). */
|
||||
yp_item_t *sub_items;
|
||||
};
|
||||
|
|
@ -178,14 +180,14 @@ typedef struct {
|
|||
const yp_item_t *key1;
|
||||
/*! Current parser event. */
|
||||
yp_event_t event;
|
||||
/*! Current binary id. */
|
||||
uint8_t id[YP_MAX_ID_LEN];
|
||||
/*! Current binary id length. */
|
||||
size_t id_len;
|
||||
/*! Current item data. */
|
||||
uint8_t data[YP_MAX_DATA_LEN];
|
||||
/*! Current binary id. */
|
||||
uint8_t id[YP_MAX_ID_LEN];
|
||||
/*! Current item data length. */
|
||||
size_t data_len;
|
||||
/*! Current item data. */
|
||||
uint8_t data[YP_MAX_DATA_LEN];
|
||||
} yp_check_ctx_t;
|
||||
|
||||
/*!
|
||||
|
|
|
|||
Loading…
Reference in a new issue