Commit d6cf9246 authored by Daniel Salzman's avatar Daniel Salzman

yparser: refactor yparser scheme, add str input checking function

parent 917fe999
......@@ -38,6 +38,31 @@
#include "libknot/internal/utils.h"
#include "libknot/yparser/yptrafo.h"
#define MAX_INCLUDE_DEPTH 5
int conf_exec_callbacks(
const yp_item_t *item,
conf_check_t *args)
{
if (item == NULL || args == NULL) {
return KNOT_EINVAL;
}
for (size_t i = 0; i < YP_MAX_MISC_COUNT; i++) {
int (*fcn)(conf_check_t *) = item->misc[i];
if (fcn == NULL) {
break;
}
int ret = fcn(args);
if (ret != KNOT_EOK) {
return ret;
}
}
return KNOT_EOK;
}
int mod_id_to_bin(
YP_TXT_BIN_PARAMS)
{
......@@ -272,14 +297,14 @@ int check_ref(
{
const char *err_str = "invalid reference";
const yp_item_t *parent = args->check->key1->var.r.ref;
const yp_item_t *ref = args->item->var.r.ref;
// Try to find the id in the referenced category.
// Try to find a referenced block with the id.
// Cannot use conf_raw_get as id is not stored in confdb directly!
int ret = conf_db_get(args->conf, args->txn, parent->name, NULL,
args->check->data, args->check->data_len, NULL);
int ret = conf_db_get(args->conf, args->txn, ref->name, NULL,
args->data, args->data_len, NULL);
if (ret != KNOT_EOK) {
*args->err_str = err_str;
args->err_str = err_str;
}
return ret;
......@@ -290,16 +315,16 @@ int check_modref(
{
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];
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];
// Try to find the module with id.
// Try to find a module with the id.
// Cannot use conf_raw_get as id is not stored in confdb directly!
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;
args->err_str = err_str;
}
return ret;
......@@ -311,10 +336,9 @@ int check_remote(
const char *err_str = "no remote address defined";
conf_val_t addr = conf_rawid_get_txn(args->conf, args->txn, C_RMT,
C_ADDR, args->previous->id,
args->previous->id_len);
C_ADDR, args->id, args->id_len);
if (conf_val_count(&addr) == 0) {
*args->err_str = err_str;
args->err_str = err_str;
return KNOT_EINVAL;
}
......@@ -326,17 +350,17 @@ int check_template(
{
const char *err_str = "global module is only allowed with the default template";
if (args->previous->id_len == CONF_DEFAULT_ID[0] &&
memcmp(args->previous->id, CONF_DEFAULT_ID + 1, args->previous->id_len) == 0) {
if (args->id_len == CONF_DEFAULT_ID[0] &&
memcmp(args->id, CONF_DEFAULT_ID + 1, args->id_len) == 0) {
return KNOT_EOK;
}
conf_val_t g_module = conf_rawid_get_txn(args->conf, args->txn, C_TPL,
C_GLOBAL_MODULE, args->previous->id,
args->previous->id_len);
C_GLOBAL_MODULE, args->id,
args->id_len);
if (g_module.code == KNOT_EOK) {
*args->err_str = err_str;
args->err_str = err_str;
return KNOT_EINVAL;
}
......@@ -349,13 +373,13 @@ int check_zone(
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);
C_MASTER, args->id);
conf_val_t dnssec = conf_zone_get_txn(args->conf, args->txn,
C_DNSSEC_SIGNING, args->previous->id);
C_DNSSEC_SIGNING, args->id);
// DNSSEC signing is not possible with slave zone.
if (conf_val_count(&master) > 0 && conf_bool(&dnssec)) {
*args->err_str = err_str;
args->err_str = err_str;
return KNOT_EINVAL;
}
......@@ -375,6 +399,8 @@ static int glob_error(
int include_file(
conf_check_t *args)
{
// This function should not be called in more threads.
static int depth = 0;
glob_t glob_buf = { 0 };
int ret;
......@@ -383,13 +409,20 @@ int include_file(
return KNOT_ENOMEM;
}
// Check for include loop.
if (depth++ > MAX_INCLUDE_DEPTH) {
CONF_LOG(LOG_ERR, "include loop detected");
ret = KNOT_EPARSEFAIL;
goto include_error;
}
// Prepare absolute include path.
if (args->check->data[0] == '/') {
if (args->data[0] == '/') {
ret = snprintf(path, PATH_MAX, "%.*s",
(int)args->check->data_len, args->check->data);
(int)args->data_len, args->data);
} else {
const char *file_name = args->parser->file.name != NULL ?
args->parser->file.name : "./";
const char *file_name = args->file_name != NULL ?
args->file_name : "./";
char *full_current_name = realpath(file_name, NULL);
if (full_current_name == NULL) {
ret = KNOT_ENOMEM;
......@@ -398,7 +431,7 @@ int include_file(
ret = snprintf(path, PATH_MAX, "%s/%.*s",
dirname(full_current_name),
(int)args->check->data_len, args->check->data);
(int)args->data_len, args->data);
free(full_current_name);
}
if (ret <= 0 || ret >= PATH_MAX) {
......@@ -434,7 +467,7 @@ int include_file(
// Include regular file.
ret = conf_parse(args->conf, args->txn, glob_buf.gl_pathv[i],
true, args->include_depth, args->previous);
true, args);
if (ret != KNOT_EOK) {
goto include_error;
}
......@@ -444,6 +477,7 @@ int include_file(
include_error:
globfree(&glob_buf);
free(path);
depth--;
return ret;
}
......@@ -31,25 +31,23 @@
#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 yp_parser_t *parser;
const yp_check_ctx_t *check;
size_t *include_depth;
conf_previous_t *previous;
const char **err_str;
const yp_item_t *item;
const uint8_t *id;
size_t id_len;
const uint8_t *data;
size_t data_len;
const char *file_name;
size_t line;
const char *err_str;
} conf_check_t;
typedef int conf_check_f(conf_check_t *);
int conf_exec_callbacks(
const yp_item_t *item,
conf_check_t *args
);
int mod_id_to_bin(
YP_TXT_BIN_PARAMS
......
......@@ -35,10 +35,9 @@ const yp_item_t scheme_mod_dnsproxy[] = {
int check_mod_dnsproxy(conf_check_t *args)
{
conf_val_t rmt = conf_rawid_get_txn(args->conf, args->txn, C_MOD_DNSPROXY,
MOD_REMOTE, args->previous->id,
args->previous->id_len);
MOD_REMOTE, args->id, args->id_len);
if (rmt.code != KNOT_EOK) {
*args->err_str = "no remote server specified";
args->err_str = "no remote server specified";
return KNOT_EINVAL;
}
......
......@@ -38,10 +38,9 @@ const yp_item_t scheme_mod_dnstap[] = {
int check_mod_dnstap(conf_check_t *args)
{
conf_val_t sink = conf_rawid_get_txn(args->conf, args->txn, C_MOD_DNSTAP,
MOD_SINK, args->previous->id,
args->previous->id_len);
MOD_SINK, args->id, args->id_len);
if (sink.code != KNOT_EOK) {
*args->err_str = "no sink specified";
args->err_str = "no sink specified";
return KNOT_EINVAL;
}
......
......@@ -37,10 +37,9 @@ const yp_item_t scheme_mod_rosedb[] = {
int check_mod_rosedb(conf_check_t *args)
{
conf_val_t dir = conf_rawid_get_txn(args->conf, args->txn, C_MOD_ROSEDB,
MOD_DBDIR, args->previous->id,
args->previous->id_len);
MOD_DBDIR, args->id, args->id_len);
if (dir.code != KNOT_EOK) {
*args->err_str = "no database directory specified";
args->err_str = "no database directory specified";
return KNOT_EINVAL;
}
......
......@@ -42,8 +42,8 @@ static const lookup_table_t synthetic_types[] = {
int check_prefix(conf_check_t *args)
{
if (strchr((const char *)args->check->data, '.') != NULL) {
*args->err_str = "dot '.' is not allowed";
if (strchr((const char *)args->data, '.') != NULL) {
args->err_str = "dot '.' is not allowed";
return KNOT_EINVAL;
}
......@@ -66,32 +66,29 @@ int check_mod_synth_record(conf_check_t *args)
{
// Check type.
conf_val_t type = conf_rawid_get_txn(args->conf, args->txn, C_MOD_SYNTH_RECORD,
MOD_TYPE, args->previous->id,
args->previous->id_len);
MOD_TYPE, args->id, args->id_len);
if (type.code != KNOT_EOK) {
*args->err_str = "no synthesis type specified";
args->err_str = "no synthesis type specified";
return KNOT_EINVAL;
}
// Check origin.
conf_val_t origin = conf_rawid_get_txn(args->conf, args->txn, C_MOD_SYNTH_RECORD,
MOD_ORIGIN, args->previous->id,
args->previous->id_len);
MOD_ORIGIN, args->id, args->id_len);
if (origin.code != KNOT_EOK && conf_opt(&type) == SYNTH_REVERSE) {
*args->err_str = "no origin specified";
args->err_str = "no origin specified";
return KNOT_EINVAL;
}
if (origin.code == KNOT_EOK && conf_opt(&type) == SYNTH_FORWARD) {
*args->err_str = "origin not allowed with forward type";
args->err_str = "origin not allowed with forward type";
return KNOT_EINVAL;
}
// Check network subnet.
conf_val_t net = conf_rawid_get_txn(args->conf, args->txn, C_MOD_SYNTH_RECORD,
MOD_NET, args->previous->id,
args->previous->id_len);
MOD_NET, args->id, args->id_len);
if (net.code != KNOT_EOK) {
*args->err_str = "no network subnet specified";
args->err_str = "no network subnet specified";
return KNOT_EINVAL;
}
......
......@@ -85,6 +85,9 @@ static int set_grp_item(
break;
}
// Set the parent item.
dst->sub_items[i].parent = dst;
if (ret != KNOT_EOK) {
free(dst->sub_items);
dst->sub_items = NULL;
......@@ -241,127 +244,97 @@ yp_check_ctx_t* yp_scheme_check_init(
return ctx;
}
static int check_key0(
const char *key,
size_t key_len,
const char *data,
size_t data_len,
yp_check_ctx_t *ctx)
static void reset_ctx(
yp_check_ctx_t *ctx,
size_t index)
{
const yp_item_t *key0 = find_item(key, key_len, ctx->scheme);
if (key0 == NULL) {
return KNOT_YP_EINVAL_ITEM;
}
assert(index < YP_MAX_NODE_DEPTH);
// Group cannot have data.
if (key0->type == YP_TGRP && data_len != 0) {
return KNOT_YP_ENOTSUP_DATA;
}
yp_node_t *node = &ctx->nodes[index];
ctx->key0 = key0;
ctx->key1 = NULL;
ctx->id_len = 0;
ctx->data_len = sizeof(((yp_check_ctx_t *)NULL)->data);
node->parent = (index > 0) ? &ctx->nodes[index - 1] : NULL;
node->item = NULL;
node->id_len = 0;
node->data_len = 0;
return yp_item_to_bin(key0, data, data_len, ctx->data, &ctx->data_len);
ctx->current = index;
}
static int check_key1(
static int check_item(
const char *key,
size_t key_len,
const char *data,
size_t data_len,
yp_check_ctx_t *ctx)
yp_check_ctx_t *ctx,
bool allow_key1_without_id)
{
// Sub-item must have a parent item.
if (ctx->key0 == NULL || ctx->key0->type != YP_TGRP) {
return KNOT_YP_EINVAL_ITEM;
}
yp_node_t *node = &ctx->nodes[ctx->current];
yp_node_t *parent = node->parent;
bool is_id = false;
if (parent != NULL) {
// Check if valid group parent.
if (parent->item->type != YP_TGRP) {
return KNOT_YP_EINVAL_ITEM;
}
const yp_item_t *key1 = find_item(key, key_len, ctx->key0->sub_items);
if (key1 == NULL) {
return KNOT_YP_EINVAL_ITEM;
// Check if valid subitem.
node->item = find_item(key, key_len, parent->item->sub_items);
} else {
node->item = find_item(key, key_len, ctx->scheme);
}
// Sub-item must not be a group.
if (key1->type == YP_TGRP) {
if (node->item == NULL) {
return KNOT_YP_EINVAL_ITEM;
}
// Check if the group requires id specification.
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_ID;
}
if (ctx->id_len == 0) {
// Check if the parent requires id specification.
if (parent != NULL && parent->item->var.g.id != NULL) {
// Check if id.
if (node->item == parent->item->var.g.id) {
is_id = true;
// Move current to the parent.
--(ctx->current);
// Check for missing id.
} else if (parent->id_len == 0 && !allow_key1_without_id) {
return KNOT_YP_ENOID;
}
// Check for id if not supported.
} else if (ctx->id_len > 0) {
return KNOT_YP_ENOTSUP_ID;
}
ctx->key1 = key1;
ctx->data_len = sizeof(((yp_check_ctx_t *)NULL)->data);
return yp_item_to_bin(key1, data, data_len, ctx->data, &ctx->data_len);
}
static int check_id(
const char *key,
size_t key_len,
const char *data,
size_t data_len,
yp_check_ctx_t *ctx)
{
// Id must have a parent item.
if (ctx->key0 == NULL) {
return KNOT_YP_EINVAL_ITEM;
}
// Only group item can have id.
if (ctx->key0->type != YP_TGRP) {
return KNOT_YP_ENOTSUP_ID;
}
// Check group item without id support.
if (ctx->key0->var.g.id == NULL) {
return KNOT_YP_ENOTSUP_ID;
// Return if no data provided.
if (data == NULL) {
return KNOT_EOK;
}
const yp_item_t *id = find_item(key, key_len, ctx->key0->sub_items);
if (id == NULL) {
return KNOT_YP_EINVAL_ITEM;
// Group cannot have data.
if (data_len != 0 && node->item->type == YP_TGRP) {
return KNOT_YP_ENOTSUP_DATA;
}
// Id item must be the first one.
if (id != ctx->key0->var.g.id) {
return KNOT_YP_EINVAL_ID;
}
// Convert item data to binary format.
const yp_item_t *item = (node->item->type != YP_TREF) ?
node->item : node->item->var.r.ref->var.g.id;
if (is_id) {
// Textual id must not be empty.
if (data_len == 0) {
return KNOT_YP_ENODATA;
}
// Textual id must not be empty.
if (data_len == 0) {
return KNOT_YP_ENODATA;
}
parent->id_len = sizeof(((yp_node_t *)NULL)->id);
int ret = yp_item_to_bin(item, data, data_len, parent->id,
&parent->id_len);
ctx->key1 = ctx->key0->var.g.id;
ctx->data_len = 0;
ctx->id_len = sizeof(((yp_check_ctx_t *)NULL)->data);
// Binary id must not be empty.
if (ret == KNOT_EOK && parent->id_len == 0) {
return KNOT_YP_EINVAL_DATA;
}
int ret = yp_item_to_bin(ctx->key0->var.g.id, data, data_len, ctx->id,
&ctx->id_len);
if (ret != KNOT_EOK) {
return ret;
} else {
node->data_len = sizeof(((yp_node_t *)NULL)->data);
int ret = yp_item_to_bin(item, data, data_len, node->data,
&node->data_len);
return ret;
}
// Binary id must not be empty.
if (ctx->id_len == 0) {
return KNOT_YP_EINVAL_DATA;
}
return KNOT_EOK;
}
int yp_scheme_check_parser(
......@@ -376,27 +349,121 @@ int yp_scheme_check_parser(
switch (parser->event) {
case YP_EKEY0:
ret = check_key0(parser->key, parser->key_len,
parser->data, parser->data_len, ctx);
reset_ctx(ctx, 0);
ret = check_item(parser->key, parser->key_len, parser->data,
parser->data_len, ctx, false);
break;
case YP_EKEY1:
ret = check_key1(parser->key, parser->key_len,
parser->data, parser->data_len, ctx);
reset_ctx(ctx, 1);
ret = check_item(parser->key, parser->key_len, parser->data,
parser->data_len, ctx, false);
if (ret != KNOT_EOK) {
break;
}
// Check for KEY1 event with id item.
if (ctx->current != 1) {
return KNOT_YP_ENOTSUP_ID;
}
break;
case YP_EID:
ret = check_id(parser->key, parser->key_len,
parser->data, parser->data_len, ctx);
reset_ctx(ctx, 1);
ret = check_item(parser->key, parser->key_len, parser->data,
parser->data_len, ctx, false);
if (ret != KNOT_EOK) {
break;
}
// Check for ID event with nonid item.
if (ctx->current != 0) {
return KNOT_YP_EINVAL_ID;
}
break;
default:
ret = KNOT_EPARSEFAIL;
break;
}
ctx->event = parser->event;
return ret;
}
int yp_scheme_check_str(
yp_check_ctx_t *ctx,
const char *key0,
const char *key1,
const char *id,
const char *data)
{
if (ctx == NULL) {
return KNOT_EINVAL;
}
size_t key0_len = (key0 != NULL) ? strlen(key0) : 0;
size_t key1_len = (key1 != NULL) ? strlen(key1) : 0;
size_t id_len = (id != NULL) ? strlen(id) : 0;
size_t data_len = (data != NULL) ? strlen(data) : 0;
// Key0 must always be non-empty.
if (key0_len == 0) {
return KNOT_YP_EINVAL_ITEM;
}
// Process key0.
reset_ctx(ctx, 0);
if (key1_len == 0) {
int ret = check_item(key0, key0_len, data, data_len, ctx, false);
if (ret != KNOT_EOK) {
return ret;
}
} else {
int ret = check_item(key0, key0_len, NULL, 0, ctx, false);
if (ret != KNOT_EOK) {
return ret;
}
}
// Process id.
if (id_len != 0) {
if (ctx->nodes[0].item->type != YP_TGRP ||
ctx->nodes[0].item->var.g.id == NULL) {
return KNOT_YP_ENOTSUP_ID;
}
const yp_name_t *name = ctx->nodes[0].item->var.g.id->name;
reset_ctx(ctx, 1);
int ret = check_item(name + 1, name[0], id, id_len, ctx, true);
if (ret != KNOT_EOK) {
return ret;
}
// Check for non-id item (should not happen).
assert(ctx->current == 0);
// Check for group id with data.
if (key1_len == 0 && data != NULL) {
return KNOT_YP_ENOTSUP_DATA;
}
}
// Process key1.
if (key1_len != 0) {
reset_ctx(ctx, 1);
int ret = check_item(key1, key1_len, data, data_len, ctx, true);
if (ret != KNOT_EOK) {
return ret;
}
// Check for id in key1 with extra data.
if (ctx->current != 1 && id_len != 0 && data != NULL) {
return KNOT_YP_ENOTSUP_DATA;
}
}
return KNOT_EOK;
}
void yp_scheme_check_deinit(
yp_check_ctx_t* ctx)
{
......
......@@ -41,6 +41,8 @@
#define YP_NIL INT64_MIN
/*! Maximal number of miscellaneous callbacks/pointers. */
#define YP_MAX_MISC_COUNT 4
/*! Maximal node stack depth. */
#define YP_MAX_NODE_DEPTH 2
#define YP_TXT_BIN_PARAMS wire_ctx_t *in, wire_ctx_t *out, const uint8_t *stop
#define YP_BIN_TXT_PARAMS wire_ctx_t *in, wire_ctx_t *out
......@@ -171,20 +173,18 @@ struct yp_item {
yp_flag_t flags;
/*! Arbitrary data/callbacks. */
const void *misc[YP_MAX_MISC_COUNT];
/*! Parent item. */
yp_item_t *parent;
/*! Item group subitems (name=NULL terminated array). */
yp_item_t *sub_items;
};
/*! Context parameters for check operations. */
typedef struct {
/*! Used scheme. */
const yp_item_t *scheme;
/*! Current key0 item. */
const yp_item_t *key0;
/*! Current key1 item. */
const yp_item_t *key1;
/*! Current parser event. */
yp_event_t event;
typedef struct yp_node yp_node_t;
struct yp_node {
/*! Parent node. */
yp_node_t *parent;
/*! Node item descriptor. */
const yp_item_t *item;
/*! Current binary id length. */
size_t id_len;
/*! Current binary id. */
......@@ -193,6 +193,16 @@ typedef struct {
size_t data_len;
/*! Current item data. */
uint8_t data[YP_MAX_DATA_LEN];
};
/*! Context parameters for check operations. */
typedef struct {
/*! Used scheme. */
const yp_item_t *scheme;
/*! Index of the current node. */
size_t current;
/*! Node stack. */
yp_node_t nodes[YP_MAX_NODE_DEPTH];
} yp_check_ctx_t;
/*!
......@@ -248,7 +258,7 @@ yp_check_ctx_t* yp_scheme_check_init(
*
* If the item is correct, context also contains binary value of the item.
*
* \param[in,out] ctx New copy of the scheme.
* \param[in,out] ctx Check context.
* \param[in] parser Parser context.
*
* \return Error code, KNOT_EOK if success.
......@@ -258,16 +268,38 @@ int yp_scheme_check_parser(
const yp_parser_t *parser
);
/*!
* Checks the string data against the scheme.
*
* Description: key0[id].key1 data
*
* If the item is correct, context also contains binary value of the item.
*
* \param[in,out] ctx Check context.
* \param[in] key0 Key0 item name.
* \param[in] key1 Key1 item name.
* \param[in] id Item identifier.
* \param[in] data Item data (NULL means no data provided).
*
* \return Error code, KNOT_EOK if success.
*/
int yp_scheme_check_str(