knot-dns/tests/knot/test_confio.c
Daniel Salzman a657f110b6 knotd: remove TCP Fast Open support
This technology didn’t prove to be helpful.
2025-12-16 14:36:18 +01:00

1099 lines
34 KiB
C

/* Copyright (C) CZ.NIC, z.s.p.o. and contributors
* SPDX-License-Identifier: GPL-2.0-or-later
* For more information, see <https://www.knot-dns.cz/>
*/
#include <tap/basic.h>
#include "test_conf.h"
#include "knot/conf/confio.h"
#include "knot/conf/tools.h"
#include "libknot/yparser/yptrafo.h"
#include "contrib/string.h"
#include "contrib/openbsd/strlcat.h"
#define SKIP_OPENBSD skip("Nested transactions are not supported on OpenBSD");
#define OUT_LEN 1024
#define ZONE1 "zone1"
#define ZONE2 "zone2"
#define ZONE3 "zone3"
char *format_key(conf_io_t *io)
{
knot_dname_txt_storage_t id;
size_t id_len = sizeof(id);
// Get the textual item id.
if (io->id_len > 0 && !io->id_as_data) {
if (yp_item_to_txt(io->key0->var.g.id, io->id, io->id_len, id,
&id_len, YP_SNOQUOTE) != KNOT_EOK) {
return NULL;
}
}
// Get the item prefix.
const char *prefix = "";
switch (io->type) {
case NEW: prefix = "+"; break;
case OLD: prefix = "-"; break;
default: break;
}
// Format the item key.
return sprintf_alloc(
"%s%.*s%s%.*s%s%s%.*s",
prefix, (int)io->key0->name[0], io->key0->name + 1,
(io->id_len > 0 && !io->id_as_data ? "[" : ""),
(io->id_len > 0 && !io->id_as_data ? (int)id_len : 0), id,
(io->id_len > 0 && !io->id_as_data ? "]" : ""),
(io->key1 != NULL ? "." : ""),
(io->key1 != NULL ? (int)io->key1->name[0] : 0),
(io->key1 != NULL ? io->key1->name + 1 : ""));
}
static int append_data(const yp_item_t *item, const uint8_t *bin, size_t bin_len,
char *out, size_t out_len)
{
char buf[YP_MAX_TXT_DATA_LEN + 1] = "\0";
size_t buf_len = sizeof(buf);
int ret = yp_item_to_txt(item, bin, bin_len, buf, &buf_len, YP_SNONE);
if (ret != KNOT_EOK) {
return ret;
}
if (strlcat(out, buf, out_len) >= out_len) {
return KNOT_ESPACE;
}
return KNOT_EOK;
}
char *format_data(conf_io_t *io)
{
char out[YP_MAX_TXT_DATA_LEN + 1] = "\0";
// Return the item identifier as the item data.
if (io->id_as_data) {
if (append_data(io->key0->var.g.id, io->id, io->id_len, out,
sizeof(out)) != KNOT_EOK) {
return NULL;
}
return strdup(out);
}
// Check for no data.
if (io->data.val == NULL && io->data.bin == NULL) {
return NULL;
}
const yp_item_t *item = (io->key1 != NULL) ? io->key1 : io->key0;
// Format explicit binary data value.
if (io->data.bin != NULL) {
if (append_data(item, io->data.bin, io->data.bin_len, out,
sizeof(out)) != KNOT_EOK) {
return NULL;
}
// Format multivalued item data.
} else if (item->flags & YP_FMULTI) {
size_t values = conf_val_count(io->data.val);
for (size_t i = 0; i < values; i++) {
// Skip other values if known index (counted from 1).
if (io->data.index > 0 &&
io->data.index != i + 1) {
conf_val_next(io->data.val);
continue;
}
if (i > 0) {
if (strlcat(out, " ", sizeof(out)) >= sizeof(out)) {
return NULL;
}
}
conf_val(io->data.val);
if (append_data(item, io->data.val->data, io->data.val->len,
out, sizeof(out)) != KNOT_EOK) {
return NULL;
}
conf_val_next(io->data.val);
}
// Format singlevalued item data.
} else {
conf_val(io->data.val);
if (append_data(item, io->data.val->data, io->data.val->len, out,
sizeof(out)) != KNOT_EOK) {
return NULL;
}
}
return strdup(out);
}
static int format_item(conf_io_t *io)
{
char *out = (char *)io->misc;
// Get the item key and data strings.
char *key = format_key(io);
char *data = format_data(io);
// Format the item.
char *item = sprintf_alloc(
"%s%s%s%s",
(*out != '\0' ? "\n" : ""),
(key != NULL ? key : ""),
(data != NULL ? " = " : ""),
(data != NULL ? data : ""));
free(key);
free(data);
if (item == NULL) {
return KNOT_ENOMEM;
}
// Append the item.
if (strlcat(out, item, OUT_LEN) >= OUT_LEN) {
free(item);
return KNOT_ESPACE;
}
free(item);
return KNOT_EOK;
}
static void test_conf_io_begin(void)
{
ok(conf_io_begin(true) == KNOT_TXN_ENOTEXISTS, "begin child txn with no parent");
ok(conf()->io.txn == NULL, "check txn depth");
#if defined(__OpenBSD__)
SKIP_OPENBSD
#else
ok(conf_io_begin(false) == KNOT_EOK, "begin parent txn");
ok(conf()->io.txn == &(conf()->io.txn_stack[0]), "check txn depth");
ok(conf_io_begin(false) == KNOT_TXN_EEXISTS, "begin another parent txn");
ok(conf()->io.txn == &(conf()->io.txn_stack[0]), "check txn depth");
for (int i = 1; i < CONF_MAX_TXN_DEPTH; i++) {
ok(conf_io_begin(true) == KNOT_EOK, "begin child txn");
ok(conf()->io.txn == &(conf()->io.txn_stack[i]), "check txn depth");
}
ok(conf_io_begin(true) == KNOT_TXN_EEXISTS, "begin another child txn");
ok(conf()->io.txn == &(conf()->io.txn_stack[CONF_MAX_TXN_DEPTH - 1]),
"check txn depth");
conf_io_abort(false);
ok(conf()->io.txn == NULL, "check txn depth");
#endif
}
static void test_conf_io_abort(void)
{
#if defined(__OpenBSD__)
SKIP_OPENBSD
#else
// Test child persistence after subchild abort.
{
char idx[2] = { '0' };
ok(conf_io_begin(false) == KNOT_EOK, "begin parent txn");
ok(conf_io_set("server", "version", NULL, idx) ==
KNOT_EOK, "set single value '%s'", idx);
}
for (int i = 1; i < CONF_MAX_TXN_DEPTH; i++) {
char idx[2] = { '0' + i };
ok(conf_io_begin(true) == KNOT_EOK, "begin child txn %s", idx);
ok(conf_io_set("server", "version", NULL, idx) ==
KNOT_EOK, "set single value '%s'", idx);
}
for (int i = CONF_MAX_TXN_DEPTH - 1; i > 0; i--) {
char idx[2] = { '0' + i };
conf_io_abort(true);
conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
ok(val.code == KNOT_EOK, "check entry");
const char *data = conf_str(&val);
ok(*data == (idx[0] - 1), "compare txn data '%s'", data);
}
conf_io_abort(false);
ok(conf()->io.txn == NULL, "check txn depth");
// Test child abort with committed subchild.
ok(conf_io_begin(false) == KNOT_EOK, "begin new parent txn");
ok(conf_io_begin(true) == KNOT_EOK, "begin child txn");
ok(conf_io_begin(true) == KNOT_EOK, "begin subchild txn");
ok(conf_io_set("server", "version", NULL, "text") ==
KNOT_EOK, "set single value");
ok(conf_io_commit(true) == KNOT_EOK, "commit subchild txn");
conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
ok(val.code == KNOT_EOK, "check entry");
const char *data = conf_str(&val);
ok(strcmp(data, "text") == 0, "compare subchild txn data '%s'", data);
conf_io_abort(true);
val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
ok(val.code == KNOT_ENOENT, "check entry");
conf_io_abort(false);
// Test unchanged read_txn.
val = conf_get_txn(conf(), &conf()->read_txn, C_SERVER, C_VERSION);
ok(val.code == KNOT_ENOENT, "check entry");
#endif
}
static void test_conf_io_commit(void)
{
ok(conf_io_commit(false) == KNOT_TXN_ENOTEXISTS, "commit no txt txn");
ok(conf_io_commit(true) == KNOT_TXN_ENOTEXISTS, "commit no txt txn");
#if defined(__OpenBSD__)
SKIP_OPENBSD
#else
// Test subchild persistence after commit.
{
char idx[2] = { '0' };
ok(conf_io_begin(false) == KNOT_EOK, "begin parent txn");
ok(conf_io_set("server", "version", NULL, idx) ==
KNOT_EOK, "set single value '%s'", idx);
}
for (int i = 1; i < CONF_MAX_TXN_DEPTH; i++) {
char idx[2] = { '0' + i };
ok(conf_io_begin(true) == KNOT_EOK, "begin child txn %s", idx);
ok(conf_io_set("server", "version", NULL, idx) ==
KNOT_EOK, "set single value '%s'", idx);
}
for (int i = CONF_MAX_TXN_DEPTH - 1; i > 0; i--) {
char idx[2] = { '0' + i };
ok(conf_io_commit(true) == KNOT_EOK, "commit child txn %s", idx);
conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
ok(val.code == KNOT_EOK, "check entry");
const char *data = conf_str(&val);
ok(*data == ('0' + CONF_MAX_TXN_DEPTH - 1), "compare txn data '%s'", data);
}
ok(conf_io_commit(false) == KNOT_EOK, "commit parent txn");
ok(conf()->io.txn == NULL, "check txn depth");
// Test child persistence after parent commit.
ok(conf_io_begin(false) == KNOT_EOK, "begin new parent txn");
conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
ok(val.code == KNOT_EOK, "check entry");
char idx[2] = { '0' + CONF_MAX_TXN_DEPTH - 1 };
const char *data = conf_str(&val);
ok(strcmp(data, idx) == 0, "compare final data '%s'", data);
conf_io_abort(false);
// Test unchanged read_txn.
val = conf_get_txn(conf(), &conf()->read_txn, C_SERVER, C_VERSION);
ok(val.code == KNOT_ENOENT, "check entry");
#endif
}
static void test_conf_io_check(void)
{
conf_io_t io = { NULL };
// ERR no txn.
ok(conf_io_check(&io) ==
KNOT_TXN_ENOTEXISTS, "check without active txn");
ok(conf_io_begin(false) == KNOT_EOK, "begin txn");
// Section check.
ok(conf_io_set("remote", "id", NULL, "remote1") ==
KNOT_EOK, "set remote id");
ok(conf_io_check(&io) ==
KNOT_EINVAL, "check missing remote address");
ok(io.error.code == KNOT_EINVAL, "compare error code");
ok(conf_io_set("remote", "address", "remote1", "1.1.1.1") ==
KNOT_EOK, "set remote address");
ok(conf_io_check(&io) ==
KNOT_EOK, "check remote address");
ok(io.error.code == KNOT_EOK, "compare error code");
// Item check.
ok(conf_io_set("zone", "domain", NULL, ZONE1) ==
KNOT_EOK, "set zone domain "ZONE1);
ok(conf_io_set("zone", "master", ZONE1, "remote1") ==
KNOT_EOK, "set zone master");
ok(conf_io_check(&io) ==
KNOT_EOK, "check all");
ok(conf_io_unset("remote", NULL, NULL, NULL) ==
KNOT_EOK, "unset remotes");
ok(conf_io_check(&io) ==
KNOT_ENOENT, "check missing master remote");
ok(io.error.code == KNOT_ENOENT, "compare error code");
conf_io_abort(false);
}
static void test_conf_io_set(void)
{
// ERR no txn.
ok(conf_io_set("server", "version", NULL, "text") ==
KNOT_TXN_ENOTEXISTS, "set without active txn");
ok(conf_io_begin(false) == KNOT_EOK, "begin txn");
// ERR.
ok(conf_io_set(NULL, NULL, NULL, NULL) ==
KNOT_EINVAL, "set NULL key0");
ok(conf_io_set("", NULL, NULL, NULL) ==
KNOT_YP_EINVAL_ITEM, "set empty key0");
ok(conf_io_set("unknown", NULL, NULL, NULL) ==
KNOT_YP_EINVAL_ITEM, "set unknown key0");
ok(conf_io_set("server", "unknown", NULL, NULL) ==
KNOT_YP_EINVAL_ITEM, "set unknown key1");
ok(conf_io_set("include", NULL, NULL, NULL) ==
KNOT_YP_ENODATA, "set non-group without data");
ok(conf_io_set("server", "background-workers", NULL, "x") ==
KNOT_EINVAL, "set invalid data");
// ERR callback
ok(conf_io_set("include", NULL, NULL, "invalid") ==
KNOT_EFILE, "set invalid callback value");
// Single group, no item, no value.
ok(conf_io_set("server", NULL, NULL, NULL) ==
KNOT_ENOTSUP, "set group no value");
// Single group, no item, value.
ok(conf_io_set("server", NULL, NULL, "text") ==
KNOT_YP_ENOTSUP_DATA, "set group value");
// Single group, item, no value.
ok(conf_io_set("server", "version", NULL, NULL) ==
KNOT_YP_ENODATA, "set group item no value");
// Single group, single value.
ok(conf_io_set("server", "version", NULL, "text") ==
KNOT_EOK, "set single value");
conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
ok(val.code == KNOT_EOK, "check entry");
ok(strcmp(conf_str(&val), "text") == 0, "check entry value");
// Single group, multi value.
ok(conf_io_set("server", "listen", NULL, "1.1.1.1") ==
KNOT_EOK, "set multivalue 1");
ok(conf_io_set("server", "listen", NULL, "1.1.1.2") ==
KNOT_EOK, "set multivalue 2");
val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
ok(val.code == KNOT_EOK, "check entry");
ok(conf_val_count(&val) == 2, "check entry value count");
// Prepare dnames.
knot_dname_t *zone1 = knot_dname_from_str_alloc(ZONE1);
ok(zone1 != NULL, "create dname "ZONE1);
knot_dname_t *zone2 = knot_dname_from_str_alloc(ZONE2);
ok(zone2 != NULL, "create dname "ZONE2);
knot_dname_t *zone3 = knot_dname_from_str_alloc(ZONE3);
ok(zone3 != NULL, "create dname "ZONE3);
// Multi group no id.
ok(conf_io_set("zone", "domain", NULL, NULL) ==
KNOT_YP_ENOID, "set zone empty domain");
// Multi group ids.
ok(conf_io_set("zone", "domain", NULL, ZONE1) ==
KNOT_EOK, "set zone domain "ZONE1);
ok(conf_io_set("zone", NULL, ZONE2, NULL) ==
KNOT_EOK, "set zone domain "ZONE2);
// Multi group, single value.
ok(conf_io_set("zone", "file", ZONE1, "name") ==
KNOT_EOK, "set zone file");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1);
ok(val.code == KNOT_EOK, "check entry");
ok(strcmp(conf_str(&val), "name") == 0, "check entry value");
// Multi group, single value, bad id.
ok(conf_io_set("zone", "file", ZONE3, "name") ==
KNOT_YP_EINVAL_ID, "set zone file");
// Multi group, single value, all ids.
ok(conf_io_set("zone", "comment", NULL, "abc") ==
KNOT_EOK, "set zones comment");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
ok(val.code == KNOT_EOK, "check entry");
ok(strcmp(conf_str(&val), "abc") == 0, "check entry value");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
ok(val.code == KNOT_EOK, "check entry");
ok(strcmp(conf_str(&val), "abc") == 0, "check entry value");
// Prepare different comment.
ok(conf_io_set("zone", "domain", NULL, ZONE3) ==
KNOT_EOK, "set zone domain "ZONE3);
ok(conf_io_set("zone", "comment", ZONE3, "xyz") ==
KNOT_EOK, "set zone comment");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3);
ok(val.code == KNOT_EOK, "check entry");
ok(strcmp(conf_str(&val), "xyz") == 0, "check entry value");
knot_dname_free(zone1, NULL);
knot_dname_free(zone2, NULL);
knot_dname_free(zone3, NULL);
ok(conf_io_commit(false) == KNOT_EOK, "commit txn");
// Update read-only transaction.
ok(conf_refresh_txn(conf()) == KNOT_EOK, "update read-only txn");
}
static void test_conf_io_unset(void)
{
// ERR no txn.
ok(conf_io_unset("server", "version", NULL, "text") ==
KNOT_TXN_ENOTEXISTS, "unset without active txn");
ok(conf_io_begin(false) == KNOT_EOK, "begin txn");
// ERR.
ok(conf_io_unset("", NULL, NULL, NULL) ==
KNOT_YP_EINVAL_ITEM, "unset unknown key0");
ok(conf_io_unset("unknown", NULL, NULL, NULL) ==
KNOT_YP_EINVAL_ITEM, "unset unknown key0");
ok(conf_io_unset("server", "unknown", NULL, NULL) ==
KNOT_YP_EINVAL_ITEM, "unset unknown key1");
ok(conf_io_unset("include", NULL, NULL, "file") ==
KNOT_ENOTSUP, "unset non-group item");
ok(conf_io_unset("server", "background-workers", NULL, "x") ==
KNOT_EINVAL, "unset invalid data");
// Single group, single value.
ok(conf_io_unset("server", "version", NULL, "") ==
KNOT_ENOENT, "unset zero length text value");
conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
ok(val.code == KNOT_EOK, "check entry");
ok(conf_io_unset("server", "version", NULL, "bad text") ==
KNOT_ENOENT, "unset bad value");
val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
ok(val.code == KNOT_EOK, "check entry");
ok(conf_io_unset("server", "version", NULL, "text") ==
KNOT_EOK, "unset explicit value");
val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
ok(val.code == KNOT_ENOENT, "check entry");
// Restart transaction.
conf_io_abort(false);
ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
ok(conf_io_unset("server", "version", NULL, NULL) ==
KNOT_EOK, "unset value");
val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
ok(val.code == KNOT_ENOENT, "check entry");
// Single group, multi value.
ok(conf_io_unset("server", "listen", NULL, "9.9.9.9") ==
KNOT_ENOENT, "unset bad value");
val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
ok(val.code == KNOT_EOK, "check entry");
ok(conf_io_unset("server", "listen", NULL, "1.1.1.1") ==
KNOT_EOK, "unset explicit value");
val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
ok(val.code == KNOT_EOK, "check entry");
ok(conf_val_count(&val) == 1, "check entry value count");
ok(conf_io_unset("server", "listen", NULL, NULL) ==
KNOT_EOK, "unset value");
val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
ok(val.code == KNOT_ENOENT, "check entry");
// Restart transaction.
conf_io_abort(false);
ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
// Whole section items.
ok(conf_io_unset("server", NULL, NULL, NULL) ==
KNOT_EOK, "unset section");
val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
ok(val.code == KNOT_ENOENT, "check entry");
val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
ok(val.code == KNOT_ENOENT, "check entry");
// Restart transaction.
conf_io_abort(false);
ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
// Prepare dnames.
knot_dname_t *zone1 = knot_dname_from_str_alloc(ZONE1);
ok(zone1 != NULL, "create dname "ZONE1);
knot_dname_t *zone2 = knot_dname_from_str_alloc(ZONE2);
ok(zone2 != NULL, "create dname "ZONE2);
knot_dname_t *zone3 = knot_dname_from_str_alloc(ZONE3);
ok(zone3 != NULL, "create dname "ZONE3);
// Multi group, single value.
ok(conf_io_unset("zone", "file", ZONE1, "name") ==
KNOT_EOK, "unset zone file");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1);
ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
// Multi group, single bad value, all ids.
ok(conf_io_unset("zone", "comment", NULL, "other") ==
KNOT_EOK, "unset zones comment");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
ok(val.code == KNOT_EOK, "check entry");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
ok(val.code == KNOT_EOK, "check entry");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3);
ok(val.code == KNOT_EOK, "check entry");
// Restart transaction.
conf_io_abort(false);
ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
// Multi group, single value (not all match), all ids.
ok(conf_io_unset("zone", "comment", NULL, "abc") ==
KNOT_EOK, "unset some zones comment");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3);
ok(val.code == KNOT_EOK, "check entry");
// Restart transaction.
conf_io_abort(false);
ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
// Multi group, single value (all match), all ids.
ok(conf_io_unset("zone", "comment", NULL, NULL) ==
KNOT_EOK, "unset all zones comment");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3);
ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
// Restart transaction.
conf_io_abort(false);
ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
// Multi group, all items, specific id.
ok(conf_io_unset("zone", NULL, ZONE1, NULL) ==
KNOT_EOK, "unset zone items");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1);
ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
ok(val.code == KNOT_EOK, "check entry");
// Restart transaction.
conf_io_abort(false);
ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
// Multi group, all items, all ids.
ok(conf_io_unset("zone", NULL, NULL, NULL) ==
KNOT_EOK, "unset zone items");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1);
ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
// Restart transaction.
conf_io_abort(false);
ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
// All groups.
ok(conf_io_unset(NULL, NULL, NULL, NULL) ==
KNOT_EOK, "unset all");
val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
ok(val.code == KNOT_ENOENT, "check entry");
val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
ok(val.code == KNOT_ENOENT, "check entry");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1);
ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
knot_dname_free(zone1, NULL);
knot_dname_free(zone2, NULL);
knot_dname_free(zone3, NULL);
conf_io_abort(false);
}
static void test_conf_io_get(void)
{
const char *ref;
char out[OUT_LEN];
conf_io_t io = {
.fcn = format_item,
.misc = out
};
// ERR no txn.
ok(conf_io_get("server", "version", NULL, false, &io) ==
KNOT_TXN_ENOTEXISTS, "get without active txn");
// Get current, no active txn.
*out = '\0';
ok(conf_io_get("server", "version", NULL, true, &io) ==
KNOT_EOK, "get current without active txn");
ref = "server.version = \"text\"";
ok(strcmp(ref, out) == 0, "compare result");
ok(conf_io_begin(false) == KNOT_EOK, "begin txn");
// ERR.
ok(conf_io_get("", NULL, NULL, true, &io) ==
KNOT_YP_EINVAL_ITEM, "get empty key0");
ok(conf_io_get("unknown", NULL, NULL, true, &io) ==
KNOT_YP_EINVAL_ITEM, "get unknown key0");
ok(conf_io_get("server", "unknown", NULL, true, &io) ==
KNOT_YP_EINVAL_ITEM, "get unknown key1");
ok(conf_io_get("include", NULL, NULL, true, &io) ==
KNOT_ENOTSUP, "get non-group item");
// Update item in the active txn.
ok(conf_io_set("server", "version", NULL, "new text") ==
KNOT_EOK, "set single value");
// Get new, active txn.
*out = '\0';
ok(conf_io_get("server", "version", NULL, false, &io) ==
KNOT_EOK, "get with active txn");
ref = "server.version = \"new text\"";
ok(strcmp(ref, out) == 0, "compare result");
// Get current, active txn.
*out = '\0';
ok(conf_io_get("server", "version", NULL, true, &io) ==
KNOT_EOK, "get with active txn");
ref = "server.version = \"text\"";
ok(strcmp(ref, out) == 0, "compare result");
// Multi value.
*out = '\0';
ok(conf_io_get("server", "listen", NULL, true, &io) ==
KNOT_EOK, "get with active txn");
ref = "server.listen = \"1.1.1.1\" \"1.1.1.2\"";
ok(strcmp(ref, out) == 0, "compare result");
// Single group.
*out = '\0';
ok(conf_io_get("server", NULL, NULL, true, &io) ==
KNOT_EOK, "get with active txn");
ref = "server.version = \"text\"\n"
"server.listen = \"1.1.1.1\" \"1.1.1.2\"";
ok(strcmp(ref, out) == 0, "compare result");
// Prepare dnames.
knot_dname_t *zone1 = knot_dname_from_str_alloc(ZONE1);
ok(zone1 != NULL, "create dname "ZONE1);
// Multi group, all values, all ids.
*out = '\0';
ok(conf_io_get("zone", NULL, NULL, true, &io) ==
KNOT_EOK, "get with active txn");
ref = "zone.domain = \"zone1.\"\n"
"zone[zone1.].file = \"name\"\n"
"zone[zone1.].comment = \"abc\"\n"
"zone.domain = \"zone2.\"\n"
"zone[zone2.].comment = \"abc\"\n"
"zone.domain = \"zone3.\"\n"
"zone[zone3.].comment = \"xyz\"";
ok(strcmp(ref, out) == 0, "compare result");
// Multi group ids.
*out = '\0';
ok(conf_io_get("zone", "domain", NULL, true, &io) ==
KNOT_EOK, "get with active txn");
ref = "zone.domain = \"zone1.\"\n"
"zone.domain = \"zone2.\"\n"
"zone.domain = \"zone3.\"";
ok(strcmp(ref, out) == 0, "compare result");
// Multi group, all values, single id.
*out = '\0';
ok(conf_io_get("zone", NULL, ZONE1, true, &io) ==
KNOT_EOK, "get with active txn");
ref = "zone.domain = \"zone1.\"\n"
"zone[zone1.].file = \"name\"\n"
"zone[zone1.].comment = \"abc\"";
ok(strcmp(ref, out) == 0, "compare result");
// Multi group, single value, single id.
*out = '\0';
ok(conf_io_get("zone", "file", ZONE1, true, &io) ==
KNOT_EOK, "get with active txn");
ref = "zone[zone1.].file = \"name\"";
ok(strcmp(ref, out) == 0, "compare result");
// All groups.
*out = '\0';
ok(conf_io_get(NULL, NULL, NULL, true, &io) ==
KNOT_EOK, "get with active txn");
ref = "server.version = \"text\"\n"
"server.listen = \"1.1.1.1\" \"1.1.1.2\"\n"
"zone.domain = \"zone1.\"\n"
"zone[zone1.].file = \"name\"\n"
"zone[zone1.].comment = \"abc\"\n"
"zone.domain = \"zone2.\"\n"
"zone[zone2.].comment = \"abc\"\n"
"zone.domain = \"zone3.\"\n"
"zone[zone3.].comment = \"xyz\"";
ok(strcmp(ref, out) == 0, "compare result");
knot_dname_free(zone1, NULL);
conf_io_abort(false);
}
static void test_conf_io_diff(void)
{
const char *ref;
char out[OUT_LEN];
conf_io_t io = {
.fcn = format_item,
.misc = out
};
// ERR no txn.
ok(conf_io_diff("server", "version", NULL, &io) ==
KNOT_TXN_ENOTEXISTS, "diff without active txn");
ok(conf_io_begin(false) == KNOT_EOK, "begin txn");
// ERR.
ok(conf_io_diff("", NULL, NULL, &io) ==
KNOT_YP_EINVAL_ITEM, "diff empty key0");
ok(conf_io_diff("unknown", NULL, NULL, &io) ==
KNOT_YP_EINVAL_ITEM, "diff unknown key0");
ok(conf_io_diff("server", "unknown", NULL, &io) ==
KNOT_YP_EINVAL_ITEM, "diff unknown key1");
ok(conf_io_diff("include", NULL, NULL, &io) ==
KNOT_ENOTSUP, "diff non-group item");
*out = '\0';
ok(conf_io_diff(NULL, NULL, NULL, &io) == KNOT_EOK, "diff no change");
ref = "";
ok(strcmp(ref, out) == 0, "compare result");
// Update singlevalued item.
ok(conf_io_set("server", "version", NULL, "new text") ==
KNOT_EOK, "set single value");
*out = '\0';
ok(conf_io_diff("server", "version", NULL, &io) == KNOT_EOK, "diff single item");
ref = "-server.version = \"text\"\n"
"+server.version = \"new text\"";
ok(strcmp(ref, out) == 0, "compare result");
// Update multivalued item.
ok(conf_io_unset("server", "listen", NULL, "1.1.1.1") ==
KNOT_EOK, "unset multivalue");
ok(conf_io_set("server", "listen", NULL, "1.1.1.3") ==
KNOT_EOK, "set multivalue");
*out = '\0';
ok(conf_io_diff("server", "listen", NULL, &io) == KNOT_EOK, "diff multi item");
ref = "-server.listen = \"1.1.1.1\" \"1.1.1.2\"\n"
"+server.listen = \"1.1.1.2\" \"1.1.1.3\"";
ok(strcmp(ref, out) == 0, "compare result");
// Unset single item.
ok(conf_io_unset("zone", "comment", ZONE3, NULL) ==
KNOT_EOK, "unset multivalue");
*out = '\0';
ok(conf_io_diff("zone", NULL, ZONE3, &io) == KNOT_EOK, "diff section");
ref = "-zone[zone3.].comment = \"xyz\"";
ok(strcmp(ref, out) == 0, "compare result");
// Unset id.
ok(conf_io_unset("zone", NULL, ZONE1, NULL) ==
KNOT_EOK, "unset id");
ok(conf_io_unset("zone", NULL, ZONE2, NULL) ==
KNOT_EOK, "unset id");
*out = '\0';
ok(conf_io_diff("zone", NULL, ZONE2, &io) == KNOT_EOK, "diff id section");
ref = "-zone.domain = \"zone2.\"\n"
"-zone[zone2.].comment = \"abc\"";
ok(strcmp(ref, out) == 0, "compare result");
*out = '\0';
ok(conf_io_diff("zone", "domain", NULL, &io) == KNOT_EOK, "diff id");
ref = "-zone.domain = \"zone1.\"\n"
"-zone.domain = \"zone2.\"";
ok(strcmp(ref, out) == 0, "compare result");
*out = '\0';
ok(conf_io_diff(NULL, NULL, NULL, &io) == KNOT_EOK, "diff whole change");
ref = "-server.version = \"text\"\n"
"+server.version = \"new text\"\n"
"-server.listen = \"1.1.1.1\" \"1.1.1.2\"\n"
"+server.listen = \"1.1.1.2\" \"1.1.1.3\"\n"
"-zone.domain = \"zone1.\"\n"
"-zone[zone1.].file = \"name\"\n"
"-zone[zone1.].comment = \"abc\"\n"
"-zone.domain = \"zone2.\"\n"
"-zone[zone2.].comment = \"abc\"\n"
"-zone[zone3.].comment = \"xyz\"";
ok(strcmp(ref, out) == 0, "compare result");
conf_io_abort(false);
}
static void test_conf_io_list(void)
{
const char *ref;
char out[OUT_LEN];
conf_io_t io = {
.fcn = format_item,
.misc = out
};
// ERR.
ok(conf_io_list("", NULL, NULL, false, true, &io) ==
KNOT_YP_EINVAL_ITEM, "list empty key0");
ok(conf_io_list("unknown", NULL, NULL, false, true, &io) ==
KNOT_YP_EINVAL_ITEM, "list unknown key0");
ok(conf_io_list("include", NULL, NULL, false, true, &io) ==
KNOT_EOK, "list non-group item");
ok(conf_io_list("template", NULL, NULL, false, false, &io) ==
KNOT_TXN_ENOTEXISTS, "no active txn");
// Desc schema.
*out = '\0';
ok(conf_io_list(NULL, NULL, NULL, true, true, &io) ==
KNOT_EOK, "list schema");
ref = "server\n"
"xdp\n"
"control\n"
"database\n"
"remote\n"
"template\n"
"zone\n"
"include";
ok(strcmp(ref, out) == 0, "compare result");
// Desc group.
*out = '\0';
ok(conf_io_list("server", NULL, NULL, true, true, &io) ==
KNOT_EOK, "list group");
ref = "server.version\n"
"server.listen\n"
"server.tcp-idle-timeout\n"
"server.tcp-io-timeout\n"
"server.tcp-remote-io-timeout\n"
"server.tcp-max-clients\n"
"server.tcp-reuseport\n"
"server.quic-max-clients\n"
"server.quic-idle-close-timeout\n"
"server.quic-outbuf-max-size\n"
"server.socket-affinity\n"
"server.udp-workers\n"
"server.tcp-workers\n"
"server.background-workers\n"
"server.udp-max-payload\n"
"server.udp-max-payload-ipv4\n"
"server.udp-max-payload-ipv6\n"
"server.edns-client-subnet\n"
"server.answer-rotation\n"
"server.automatic-acl\n"
"server.dbus-event";
ok(strcmp(ref, out) == 0, "compare result");
// List item options.
*out = '\0';
ok(conf_io_list("zone", "zonefile-load", NULL, true, true, &io) ==
KNOT_EOK, "list item options");
ref = "zone.zonefile-load = \"opt1\"\n"
"zone.zonefile-load = \"opt2\"";
ok(strcmp(ref, out) == 0, "compare result");
// List zone identifiers 1.
*out = '\0';
ok(conf_io_list("zone", NULL, NULL, false, true, &io) ==
KNOT_EOK, "list zone identifiers 1");
ref = "zone[zone1.]\n"
"zone[zone2.]\n"
"zone[zone3.]";
ok(strcmp(ref, out) == 0, "compare result");
// List zone identifiers 2.
*out = '\0';
ok(conf_io_list("zone", "domain", NULL, false, true, &io) ==
KNOT_EOK, "list zone identifiers 2");
ref = "zone = \"zone1.\"\n"
"zone = \"zone2.\"\n"
"zone = \"zone3.\"";
ok(strcmp(ref, out) == 0, "compare result");
// List item values.
*out = '\0';
ok(conf_io_list("server", "listen", NULL, false, true, &io) ==
KNOT_EOK, "list item values");
ref = "server.listen = \"1.1.1.1\" \"1.1.1.2\"";
ok(strcmp(ref, out) == 0, "compare result");
// List multi-group item values.
*out = '\0';
ok(conf_io_list("zone", "file", "zone1", false, true, &io) ==
KNOT_EOK, "list multi-group item values");
ref = "zone[zone1.].file = \"name\"";
ok(strcmp(ref, out) == 0, "compare result");
}
static const yp_item_t desc_server[] = {
{ C_VERSION, YP_TSTR, YP_VNONE },
{ C_LISTEN, YP_TADDR, YP_VNONE, YP_FMULTI },
// Required config cache items - assert fix.
{ C_TCP_IDLE_TIMEOUT, YP_TINT, YP_VNONE },
{ C_TCP_IO_TIMEOUT, YP_TINT, YP_VNONE },
{ C_TCP_RMT_IO_TIMEOUT, YP_TINT, YP_VNONE },
{ C_TCP_MAX_CLIENTS, YP_TINT, YP_VNONE },
{ C_TCP_REUSEPORT, YP_TBOOL, YP_VNONE },
{ C_QUIC_MAX_CLIENTS, YP_TINT, YP_VNONE },
{ C_QUIC_IDLE_CLOSE, YP_TINT, YP_VNONE },
{ C_QUIC_OUTBUF_MAX_SIZE, YP_TINT, YP_VNONE },
{ C_SOCKET_AFFINITY, YP_TBOOL, YP_VNONE },
{ C_UDP_WORKERS, YP_TINT, YP_VNONE },
{ C_TCP_WORKERS, YP_TINT, YP_VNONE },
{ C_BG_WORKERS, YP_TINT, YP_VNONE },
{ C_UDP_MAX_PAYLOAD, YP_TINT, YP_VNONE },
{ C_UDP_MAX_PAYLOAD_IPV4, YP_TINT, YP_VNONE },
{ C_UDP_MAX_PAYLOAD_IPV6, YP_TINT, YP_VNONE },
{ C_ECS, YP_TBOOL, YP_VNONE },
{ C_ANS_ROTATION, YP_TBOOL, YP_VNONE },
{ C_AUTO_ACL, YP_TBOOL, YP_VNONE },
{ C_DBUS_EVENT, YP_TOPT, YP_VNONE },
{ NULL }
};
static const yp_item_t desc_xdp[] = {
{ C_UDP, YP_TBOOL, YP_VNONE },
{ C_TCP, YP_TBOOL, YP_VNONE },
{ C_QUIC, YP_TBOOL, YP_VNONE },
{ C_TCP_MAX_CLIENTS, YP_TINT, YP_VNONE },
{ C_TCP_INBUF_MAX_SIZE, YP_TINT, YP_VNONE },
{ C_TCP_OUTBUF_MAX_SIZE,YP_TINT, YP_VNONE },
{ C_TCP_IDLE_CLOSE, YP_TINT, YP_VNONE },
{ C_TCP_IDLE_RESET, YP_TINT, YP_VNONE },
{ C_TCP_RESEND, YP_TINT, YP_VNONE },
{ C_ROUTE_CHECK, YP_TBOOL, YP_VNONE },
{ C_RING_SIZE, YP_TINT, YP_VNONE },
{ C_BUSYPOLL_BUDGET, YP_TINT, YP_VNONE },
{ C_BUSYPOLL_TIMEOUT, YP_TINT, YP_VNONE },
{ NULL }
};
static const yp_item_t desc_control[] = {
{ C_TIMEOUT, YP_TINT, YP_VNONE },
{ NULL }
};
static const yp_item_t desc_remote[] = {
{ C_ID, YP_TSTR, YP_VNONE },
{ C_ADDR, YP_TADDR, YP_VNONE, YP_FMULTI },
{ NULL }
};
static const yp_item_t desc_database[] = {
{ C_TIMER_DB_SYNC, YP_TOPTINT, YP_VNONE },
{ NULL }
};
static const knot_lookup_t opts[] = {
{ 1, "opt1" },
{ 2, "opt2" },
{ 0, NULL }
};
#define ZONE_ITEMS \
{ C_FILE, YP_TSTR, YP_VNONE }, \
{ C_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI, { check_ref } }, \
{ C_ZONEFILE_LOAD, YP_TOPT, YP_VOPT = { opts, 0 } }, \
{ C_JOURNAL_CONTENT, YP_TOPT, YP_VOPT = { opts, 0 } }, \
{ C_DNSSEC_SIGNING, YP_TBOOL, YP_VNONE }, \
{ C_DNSSEC_VALIDATION, YP_TBOOL, YP_VNONE }, \
{ C_SERIAL_MODULO, YP_TSTR, YP_VSTR = { "0/1" } }, \
{ C_ZONEMD_VERIFY, YP_TBOOL, YP_VNONE }, \
{ C_CATALOG_ROLE, YP_TOPT, YP_VOPT = { opts, 0 } }, \
{ C_CATALOG_TPL, YP_TREF, YP_VREF = { C_RMT } }, \
{ C_COMMENT, YP_TSTR, YP_VNONE },
static const yp_item_t desc_template[] = {
{ C_ID, YP_TSTR, YP_VNONE },
ZONE_ITEMS
{ NULL }
};
static const yp_item_t desc_zone[] = {
{ C_DOMAIN, YP_TDNAME, YP_VNONE },
ZONE_ITEMS
{ NULL }
};
const yp_item_t test_schema[] = {
{ C_SRV, YP_TGRP, YP_VGRP = { desc_server } },
{ C_XDP, YP_TGRP, YP_VGRP = { desc_xdp } },
{ C_CTL, YP_TGRP, YP_VGRP = { desc_control } },
{ C_DB, YP_TGRP, YP_VGRP = { desc_database } },
{ C_RMT, YP_TGRP, YP_VGRP = { desc_remote }, YP_FMULTI, { check_remote } },
{ C_TPL, YP_TGRP, YP_VGRP = { desc_template }, YP_FMULTI, { check_template } },
{ C_ZONE, YP_TGRP, YP_VGRP = { desc_zone }, YP_FMULTI, { check_zone } },
{ C_INCL, YP_TSTR, YP_VNONE, YP_FNONE, { include_file } },
{ NULL }
};
int main(int argc, char *argv[])
{
plan_lazy();
ok(test_conf("", test_schema) == KNOT_EOK, "Prepare configuration");
diag("conf_io_begin");
test_conf_io_begin();
diag("conf_io_abort");
test_conf_io_abort();
diag("conf_io_commit");
test_conf_io_commit();
diag("conf_io_check");
test_conf_io_check();
diag("conf_io_set");
test_conf_io_set();
diag("conf_io_unset");
test_conf_io_unset();
diag("conf_io_get");
test_conf_io_get();
diag("conf_io_diff");
test_conf_io_diff();
diag("conf_io_list");
test_conf_io_list();
conf_free(conf());
return 0;
}