diff --git a/doc/man/knot.conf.5in b/doc/man/knot.conf.5in index 7e0f62a3f..de3207214 100644 --- a/doc/man/knot.conf.5in +++ b/doc/man/knot.conf.5in @@ -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 diff --git a/doc/reference.rst b/doc/reference.rst index 81b4cafb6..b72f06709 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -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` 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`. + 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 diff --git a/src/knot/conf/base.c b/src/knot/conf/base.c index cddba0a4c..9ec2d6649 100644 --- a/src/knot/conf/base.c +++ b/src/knot/conf/base.c @@ -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; diff --git a/src/knot/conf/base.h b/src/knot/conf/base.h index de02b4be0..ea803dd8b 100644 --- a/src/knot/conf/base.h +++ b/src/knot/conf/base.h @@ -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 ); /*! diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c index a42c743f9..42f01bf37 100644 --- a/src/knot/conf/conf.c +++ b/src/knot/conf/conf.c @@ -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; } diff --git a/src/knot/conf/scheme.c b/src/knot/conf/scheme.c index 2f402d118..ae12dc171 100644 --- a/src/knot/conf/scheme.c +++ b/src/knot/conf/scheme.c @@ -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 } }; diff --git a/src/knot/conf/tools.c b/src/knot/conf/tools.c index feca79f6c..9a7198661 100644 --- a/src/knot/conf/tools.c +++ b/src/knot/conf/tools.c @@ -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; } diff --git a/src/knot/conf/tools.h b/src/knot/conf/tools.h index e6ab42657..2f230dc6d 100644 --- a/src/knot/conf/tools.h +++ b/src/knot/conf/tools.h @@ -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 ); diff --git a/src/knot/modules/synth_record.c b/src/knot/modules/synth_record.c index 4e1209796..7b3e2cd65 100644 --- a/src/knot/modules/synth_record.c +++ b/src/knot/modules/synth_record.c @@ -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; } diff --git a/src/libknot/errcode.c b/src/libknot/errcode.c index 41e27e45c..d6927c246 100644 --- a/src/libknot/errcode.c +++ b/src/libknot/errcode.c @@ -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" }, diff --git a/src/libknot/internal/errcode.h b/src/libknot/internal/errcode.h index 74f140a84..60b007dcd 100644 --- a/src/libknot/internal/errcode.h +++ b/src/libknot/internal/errcode.h @@ -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, diff --git a/src/libknot/yparser/ypscheme.c b/src/libknot/yparser/ypscheme.c index bbbb484b3..0b5fbb7f1 100644 --- a/src/libknot/yparser/ypscheme.c +++ b/src/libknot/yparser/ypscheme.c @@ -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. diff --git a/src/libknot/yparser/ypscheme.h b/src/libknot/yparser/ypscheme.h index ff7030e6e..a0470a615 100644 --- a/src/libknot/yparser/ypscheme.h +++ b/src/libknot/yparser/ypscheme.h @@ -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; /*!