diff --git a/Knot.files b/Knot.files index e18b95993afb21e9a16ea3c6652103e5f5e37348..613f302748008d6923e4cbad06aebf36d947eba3 100644 --- a/Knot.files +++ b/Knot.files @@ -1,5 +1,3 @@ -Doxyfile -KNOWN_ISSUES Makefile.am README configure.ac @@ -73,6 +71,11 @@ src/common/mem.c src/common/mem.h src/common/mempool.c src/common/mempool.h +src/common/namedb/namedb.h +src/common/namedb/namedb_lmdb.c +src/common/namedb/namedb_lmdb.h +src/common/namedb/namedb_trie.c +src/common/namedb/namedb_trie.h src/dnstap/Makefile.am src/dnstap/convert.c src/dnstap/convert.h @@ -176,8 +179,6 @@ src/knot/worker/queue.c src/knot/worker/queue.h src/knot/zone/contents.c src/knot/zone/contents.h -src/knot/zone/events.c -src/knot/zone/events.h src/knot/zone/node.c src/knot/zone/node.h src/knot/zone/semantic-check.c @@ -198,6 +199,14 @@ src/knot/zone/zonedb.c src/knot/zone/zonedb.h src/knot/zone/zonefile.c src/knot/zone/zonefile.h +src/knot/zone/events/events.c +src/knot/zone/events/events.h +src/knot/zone/events/handlers.h +src/knot/zone/events/handlers.c +src/knot/zone/events/replan.c +src/knot/zone/events/replan.h +src/knot/zone/timers.c +src/knot/zone/timers.h src/libknot/binary.c src/libknot/binary.h src/libknot/common.h @@ -322,6 +331,7 @@ tests/fdset.c tests/hattrie.c tests/hhash.c tests/journal.c +tests/namedb.c tests/node.c tests/pkt.c tests/process_answer.c @@ -343,3 +353,4 @@ tests/zone_events.c tests/zone_update.c tests/zonedb.c tests/ztree.c +tests/zone_timers.c diff --git a/configure.ac b/configure.ac index 95aff9fe068a52e7b9c5c8855080439d4c1d1b89..1c463f8cac996d33d4eb83026b1e58cdbf781c8b 100644 --- a/configure.ac +++ b/configure.ac @@ -312,6 +312,10 @@ dt_DNSTAP([ ]) AM_CONDITIONAL([HAVE_DNSTAP], test "$opt_dnstap" != "no") +dnl Check for LMDB +KNOT_CHECK_HEADER([lmdb], [LMDB], [auto], [lmdb.h], [], [-llmdb]) +AS_IF([test "$enable_lmdb" = yes], [AC_DEFINE([HAVE_LMDB], [1], [Define to 1 to enable LMDB support])]) + AC_SEARCH_LIBS([pow], [m]) AC_SEARCH_LIBS([pthread_create], [pthread], [], [AC_MSG_ERROR([pthreads not found])]) AC_SEARCH_LIBS([dlopen], [dl]) @@ -407,6 +411,7 @@ AC_MSG_RESULT([ Systemd integration: ${enable_systemd} Dnstap support: ${opt_dnstap} Code coverage: ${enable_code_coverage} + LMDB support: ${enable_lmdb} Continue with 'make' command ]) diff --git a/doc/running.rst b/doc/running.rst index 496834ed95496e1155272213b85ee02b4a603241..0a80081e5c001fc1ffbd22698ed6c21d1b655e64 100644 --- a/doc/running.rst +++ b/doc/running.rst @@ -45,14 +45,13 @@ If you want to control the daemon directly, use ``SIGINT`` to quit the process o -f, --force Force operation - override some checks. -v, --verbose Verbose mode - additional runtime information. -V, --version Print knot server version. - -i, --interactive Interactive mode (do not daemonize). -h, --help Print help and usage. Actions: stop Stop server. reload Reload configuration and changed zones. refresh <zone> Refresh slave zone (all if not specified). - flush Flush journal and update zone files. + flush <zone> Flush journal and update zone files. (all if not specified) status Check if server is running. zonestatus Show status of configured zones. checkconf Check current server configuration. diff --git a/m4/knot-check-header.m4 b/m4/knot-check-header.m4 new file mode 100644 index 0000000000000000000000000000000000000000..884b986043c6a265130b06619f19c1151edb92fc --- /dev/null +++ b/m4/knot-check-header.m4 @@ -0,0 +1,49 @@ +# KNOT_CHECK_HEADER([prefix], [name], [default], [header], [cflags], [libs]) +# ----------------------------------------------------------------------------- +# Check presence of a library by checking for a header file. +# +# - adds --enable-prefix configure flag +# +# - if $enable_prefix is yes or auto, checks for the header file +# +# - emits an error if $enable_prefix is yes and the header is not present +# +# - check can be overridden by setting prefix_CFLAGS and prefix_LIBS +# environment variables +# +# Output variables: $enable_foo (yes or no), $foo_CFLAGS, and $foo_LIBS +# +AC_DEFUN([KNOT_CHECK_HEADER], +[ + AC_ARG_ENABLE([$1], AC_HELP_STRING([--enable-$1], [Support for $2 [default $3]]), + [enable_][$1][=$enableval], [enable_][$1][=][$3]) + + AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $2, overriding defaults]) + AC_ARG_VAR([$1][_LIBS], [linker flags for $2, overriding defaults]) + + AS_CASE([$enable_][$1], + [no], [ + [$1][_CFLAGS]= + [$1][_LIBS]= + ], + [auto|yes], [ + AS_IF([test -n "$][$1][_LIBS"], [ + dnl: skip header check if environment variable is set + [enable_][$1][=yes] + ],[ + dnl: check for header + AC_CHECK_HEADER([$4], [ + [enable_][$1]=yes + [$1][_CFLAGS]=[$5] + [$1][_LIBS]=[$6] + ], [ + AS_IF([test "$enable_][$1][" = auto], + [[enable_][$1]=no], + [AC_MSG_ERROR([Header file "$4" for $2 not found])] + ) + ]) + ]) + ], + [AC_MSG_ERROR([Invalid value of --enable-$1])] + ) +]) diff --git a/man/knotc.8.in b/man/knotc.8.in index badf20ad9f6b368c6e6d7386500861d16c98cd05..543b281d1032b41b6140ebba85b7ee9ac81d8571 100644 --- a/man/knotc.8.in +++ b/man/knotc.8.in @@ -32,9 +32,6 @@ Verbose mode \- additional runtime information. \fB\-V\fR, \fB\-\-version\fR Print version of the server. .TP -\fB\-i\fR, \fB\-\-interactive\fR -Interactive mode (do not daemonize). -.TP \fB\-h\fR, \fB\-\-help\fR Print help and usage. .SS "Actions:" @@ -42,11 +39,11 @@ Print help and usage. \fBstop\fR Stop server (no\-op if not running). .TP -\fBreload\fR -Reload configuration and changed zones. +\fBreload\fR [\fIzone\fR]... +Reload configuration and changed zones (all if not specified). .TP -\fBflush\fR -Flush journal and update zone files. +\fBflush\fR [\fIzone\fR]... +Flush journal and update zone files (all if not specified). .TP \fBstatus\fR Check if server is running. diff --git a/src/Makefile.am b/src/Makefile.am index 81bbbe915d1d164a36a6f8fcb4829b88a0177893..32e2a9c85a5845b6cab98779d5c9ab8c314988ad 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -112,6 +112,11 @@ libknotcs_la_SOURCES = \ common/mem.h \ common/mempool.c \ common/mempool.h \ + common/namedb/namedb.h \ + common/namedb/namedb_lmdb.h \ + common/namedb/namedb_lmdb.c \ + common/namedb/namedb_trie.h \ + common/namedb/namedb_trie.c \ common/log.c \ common/log.h @@ -296,12 +301,18 @@ libknotd_la_SOURCES = \ knot/worker/queue.h \ knot/zone/contents.c \ knot/zone/contents.h \ - knot/zone/events.c \ - knot/zone/events.h \ + knot/zone/events/events.c \ + knot/zone/events/events.h \ + knot/zone/events/handlers.c \ + knot/zone/events/handlers.h \ + knot/zone/events/replan.c \ + knot/zone/events/replan.h \ knot/zone/node.c \ knot/zone/node.h \ knot/zone/semantic-check.c \ knot/zone/semantic-check.h \ + knot/zone/timers.c \ + knot/zone/timers.h \ knot/zone/zone-diff.c \ knot/zone/zone-diff.h \ knot/zone/zone-dump.c \ @@ -322,17 +333,20 @@ libknotd_la_SOURCES = \ # libraries libknot_la_LIBADD = libknotcs.la zscanner/libzscanner.la libknotd_la_LIBADD = libknots.la libknotcs.la libknot.la +libknotus_la_LIBADD = libknots.la libknotcs.la libknot.la +libknotd_la_CPPFLAGS = $(AM_CPPFLAGS) $(lmdb_CFLAGS) +libknotd_la_LDFLAGS = $(AM_LDFLAGS) $(lmdb_LIBS) libknotus_la_CPPFLAGS = $(AM_CPPFLAGS) $(libidn_CFLAGS) libknotus_la_LDFLAGS = $(AM_LDFLAGS) $(libidn_LIBS) -libknotcs_la_CPPFLAGS = $(AM_CPPFLAGS) $(systemd_CFLAGS) -libknotcs_la_LDFLAGS = $(AM_LDFLAGS) $(systemd_LIBS) +libknotcs_la_CPPFLAGS = $(AM_CPPFLAGS) $(systemd_CFLAGS) $(lmdb_CFLAGS) +libknotcs_la_LDFLAGS = $(AM_LDFLAGS) $(systemd_LIBS) $(lmdb_LIBS) # sbin programs -knotd_LDADD = libknot.la libknotd.la $(systemd_LIBS) +knotd_LDADD = libknot.la libknotd.la $(systemd_LIBS) $(lmdb_LIBS) knotc_LDADD = libknot.la libknotd.la # bin programs -BIN_LIBS = libknotus.la libknotcs.la libknots.la libknot.la +BIN_LIBS = libknotus.la libknots.la kdig_LDADD = $(BIN_LIBS) $(libidn_LIBS) khost_LDADD = $(BIN_LIBS) $(libidn_LIBS) knsupdate_LDADD = $(BIN_LIBS) zscanner/libzscanner.la diff --git a/src/common/log.c b/src/common/log.c index a5c8b1c8554cd81c65831618649f2fccbea19fd0..7eb73ccff6e96aa8fc6a38a148762d90d456438a 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -403,7 +403,7 @@ int log_msg_zone(int priority, const knot_dname_t *zone, const char *fmt, ...) { va_list args; va_start(args, fmt); - char *zone_str = knot_dname_to_str(zone); + char *zone_str = knot_dname_to_str_alloc(zone); int result = log_msg_text(priority, zone_str ? zone_str : LOG_NULL_ZONE_STRING, fmt, args); diff --git a/src/common/namedb/namedb.h b/src/common/namedb/namedb.h new file mode 100644 index 0000000000000000000000000000000000000000..10d2cdd53540d6798b82244ae166f28188af3a1b --- /dev/null +++ b/src/common/namedb/namedb.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "libknot/dname.h" + +enum { + KNOT_NAMEDB_RDONLY = 1 << 0, + KNOT_NAMEDB_SORTED = 1 << 1 +}; + +typedef void knot_namedb_t; +typedef void knot_iter_t; + +typedef struct knot_val { + void *data; + size_t len; +} knot_val_t; + +typedef struct knot_txn { + knot_namedb_t *db; + void *txn; +} knot_txn_t; + +struct namedb_api { + + const char *name; + + /* Context operations */ + + knot_namedb_t *(*init)(const char *handle, mm_ctx_t *mm); + void (*deinit)(knot_namedb_t *db); + + /* Transactions */ + + int (*txn_begin)(knot_namedb_t *db, knot_txn_t *txn, unsigned flags); + int (*txn_commit)(knot_txn_t *txn); + void (*txn_abort)(knot_txn_t *txn); + + /* Data access */ + + int (*count)(knot_txn_t *txn); + int (*find)(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags); + int (*insert)(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags); + int (*del)(knot_txn_t *txn,knot_val_t *key); + + /* Iteration */ + + knot_iter_t *(*iter_begin)(knot_txn_t *txn, unsigned flags); + knot_iter_t *(*iter_next)(knot_iter_t *iter); + int (*iter_key)(knot_iter_t *iter, knot_val_t *key); + int (*iter_val)(knot_iter_t *iter, knot_val_t *val); + void (*iter_finish)(knot_iter_t *iter); +}; diff --git a/src/common/namedb/namedb_lmdb.c b/src/common/namedb/namedb_lmdb.c new file mode 100644 index 0000000000000000000000000000000000000000..5e0b4ae4d34d68154ef25ceda7e6729a9c0c52e7 --- /dev/null +++ b/src/common/namedb/namedb_lmdb.c @@ -0,0 +1,315 @@ +/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_LMDB + +#include <lmdb.h> + +#include "common/namedb/namedb_lmdb.h" +#include "libknot/errcode.h" + +struct lmdb_env +{ + MDB_dbi dbi; + MDB_env *env; + mm_ctx_t *pool; +}; + +static int dbase_open(struct lmdb_env *env, const char *handle) +{ + int ret = mdb_env_create(&env->env); + if (ret != 0) { + return ret; + } + + ret = mdb_env_open(env->env, handle, 0, 0644); + if (ret != 0) { + mdb_env_close(env->env); + return ret; + } + + MDB_txn *txn = NULL; + ret = mdb_txn_begin(env->env, NULL, 0, &txn); + if (ret != 0) { + mdb_env_close(env->env); + return ret; + } + + ret = mdb_open(txn, NULL, 0, &env->dbi); + if (ret != 0) { + mdb_txn_abort(txn); + mdb_env_close(env->env); + return ret; + } + + ret = mdb_txn_commit(txn); + if (ret != 0) { + mdb_env_close(env->env); + return ret; + } + + return 0; +} + +static void dbase_close(struct lmdb_env *env) +{ + mdb_close(env->env, env->dbi); + mdb_env_close(env->env); +} + +static knot_namedb_t *init(const char *handle, mm_ctx_t *mm) +{ + struct lmdb_env *env = mm_alloc(mm, sizeof(struct lmdb_env)); + if (env == NULL) { + return NULL; + } + memset(env, 0, sizeof(struct lmdb_env)); + + int ret = dbase_open(env, handle); + if (ret != 0) { + mm_free(mm, env); + return NULL; + } + + env->pool = mm; + return env; +} + +static void deinit(knot_namedb_t *db) +{ + if (db) { + struct lmdb_env *env = db; + + dbase_close(env); + mm_free(env->pool, env); + } +} + +static int txn_begin(knot_namedb_t *db, knot_txn_t *txn, unsigned flags) +{ + txn->db = db; + txn->txn = NULL; + + unsigned txn_flags = 0; + if (flags & KNOT_NAMEDB_RDONLY) { + txn_flags |= MDB_RDONLY; + } + + struct lmdb_env *env = db; + int ret = mdb_txn_begin(env->env, NULL, txn_flags, (MDB_txn **)&txn->txn); + if (ret != 0) { + return KNOT_ERROR; + } + + return KNOT_EOK; +} + +static int txn_commit(knot_txn_t *txn) +{ + int ret = mdb_txn_commit((MDB_txn *)txn->txn); + if (ret != 0) { + return KNOT_ERROR; + } + + return KNOT_EOK; +} + +static void txn_abort(knot_txn_t *txn) +{ + mdb_txn_abort((MDB_txn *)txn->txn); +} + +static int count(knot_txn_t *txn) +{ + struct lmdb_env *env = txn->db; + + MDB_stat stat; + int ret = mdb_stat(txn->txn, env->dbi, &stat); + if (ret != 0) { + return KNOT_ERROR; + } + + return stat.ms_entries; +} + +static int find(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags) +{ + struct lmdb_env *env = txn->db; + MDB_val db_key = { key->len, key->data }; + MDB_val data = { 0, NULL }; + + + int ret = mdb_get(txn->txn, env->dbi, &db_key, &data); + if (ret != 0) { + if (ret == MDB_NOTFOUND) { + return KNOT_ENOENT; + } else { + return KNOT_ERROR; + } + } + + val->data = data.mv_data; + val->len = data.mv_size; + return KNOT_EOK; +} + +static int insert(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags) +{ + struct lmdb_env *env = txn->db; + + MDB_cursor *cursor = NULL; + int ret = mdb_cursor_open(txn->txn, env->dbi, &cursor); + if (ret != 0) { + return KNOT_ERROR; + } + + MDB_val db_key = { key->len, key->data }; + MDB_val data = { val->len, val->data }; + + ret = mdb_cursor_get(cursor, &db_key, NULL, MDB_SET); + if (ret != 0) { + mdb_cursor_close(cursor); + if (ret == MDB_NOTFOUND) { + // Insert new item + ret = mdb_put(txn->txn, env->dbi, &db_key, &data, 0); + if (ret != 0) { + return KNOT_ERROR; + } + + return KNOT_EOK; + } else { + return ret; + } + } + + ret = mdb_cursor_put(cursor, &db_key, &data, MDB_CURRENT); + mdb_cursor_close(cursor); + if (ret != 0) { + return KNOT_ERROR; + } + + return KNOT_EOK; +} + +static int del(knot_txn_t *txn, knot_val_t *key) +{ + struct lmdb_env *env = txn->db; + MDB_val db_key = { key->len, key->data }; + MDB_val data = { 0, NULL }; + + int ret = mdb_del(txn->txn, env->dbi, &db_key, &data); + if (ret != 0) { + return KNOT_ERROR; + } + + return KNOT_EOK; +} + +static knot_iter_t *iter_begin(knot_txn_t *txn, unsigned flags) +{ + struct lmdb_env *env = txn->db; + MDB_cursor *cursor = NULL; + + int ret = mdb_cursor_open(txn->txn, env->dbi, &cursor); + if (ret != 0) { + return NULL; + } + + ret = mdb_cursor_get(cursor, NULL, NULL, MDB_FIRST); + if (ret != 0) { + mdb_cursor_close(cursor); + return NULL; + } + + return cursor; +} + +static knot_iter_t *iter_next(knot_iter_t *iter) +{ + MDB_cursor *cursor = iter; + + int ret = mdb_cursor_get(cursor, NULL, NULL, MDB_NEXT); + if (ret != 0) { + mdb_cursor_close(cursor); + return NULL; + } + + return cursor; +} + +static int iter_key(knot_iter_t *iter, knot_val_t *key) +{ + MDB_cursor *cursor = iter; + + MDB_val mdb_key, mdb_val; + int ret = mdb_cursor_get(cursor, &mdb_key, &mdb_val, MDB_GET_CURRENT); + if (ret != 0) { + return KNOT_ERROR; + } + + key->data = mdb_key.mv_data; + key->len = mdb_key.mv_size; + return KNOT_EOK; +} + +static int iter_val(knot_iter_t *iter, knot_val_t *val) +{ + MDB_cursor *cursor = iter; + + MDB_val mdb_key, mdb_val; + int ret = mdb_cursor_get(cursor, &mdb_key, &mdb_val, MDB_GET_CURRENT); + if (ret != 0) { + return KNOT_ERROR; + } + + val->data = mdb_val.mv_data; + val->len = mdb_val.mv_size; + return KNOT_EOK; +} + +static void iter_finish(knot_iter_t *iter) +{ + if (iter == NULL) { + return; + } + + MDB_cursor *cursor = iter; + mdb_cursor_close(cursor); +} + +struct namedb_api *namedb_lmdb_api(void) +{ + static struct namedb_api api = { + "lmdb", + init, deinit, + txn_begin, txn_commit, txn_abort, + count, find, insert, del, + iter_begin, iter_next, iter_key, iter_val, iter_finish + }; + + return &api; +} + +#else + +#include <stdlib.h> + +struct namedb_api *namedb_lmdb_api(void) +{ + return NULL; +} + +#endif diff --git a/src/common/namedb/namedb_lmdb.h b/src/common/namedb/namedb_lmdb.h new file mode 100644 index 0000000000000000000000000000000000000000..0dcf9d64f1d8a708926d09b74435b1c96b6d8350 --- /dev/null +++ b/src/common/namedb/namedb_lmdb.h @@ -0,0 +1,21 @@ +/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "common/namedb/namedb.h" + +struct namedb_api *namedb_lmdb_api(void); diff --git a/src/common/namedb/namedb_trie.c b/src/common/namedb/namedb_trie.c new file mode 100644 index 0000000000000000000000000000000000000000..0ca29f652ccea480c562f075cebed552840af161 --- /dev/null +++ b/src/common/namedb/namedb_trie.c @@ -0,0 +1,140 @@ +/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "common/namedb/namedb_trie.h" +#include "common-knot/hattrie/hat-trie.h" +#include "libknot/errcode.h" + +static knot_namedb_t *init(const char *handle, mm_ctx_t *mm) +{ + return hattrie_create_n(TRIE_BUCKET_SIZE, mm); +} + +static void deinit(knot_namedb_t *db) +{ + hattrie_free((hattrie_t *)db); +} + +static int txn_begin(knot_namedb_t *db, knot_txn_t *txn, unsigned flags) +{ + txn->txn = (void *)(size_t)flags; + txn->db = db; + return KNOT_EOK; /* N/A */ +} + +static int txn_commit(knot_txn_t *txn) +{ + /* Rebuild order index only for WR transactions. */ + if ((size_t)txn->txn & KNOT_NAMEDB_RDONLY) { + return KNOT_EOK; + } + + hattrie_build_index((hattrie_t *)txn->db); + return KNOT_EOK; +} + +static void txn_abort(knot_txn_t *txn) +{ +} + +static int count(knot_txn_t *txn) +{ + return hattrie_weight((hattrie_t *)txn->db); +} + +static int find(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags) +{ + value_t *ret = hattrie_tryget((hattrie_t *)txn->db, key->data, key->len); + if (ret == NULL) { + return KNOT_ENOENT; + } + + val->data = *ret; + val->len = sizeof(value_t); /* Trie doesn't support storing length. */ + return KNOT_EOK; +} + +static int insert(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags) +{ + value_t *ret = hattrie_get((hattrie_t *)txn->db, key->data, key->len); + if (ret == NULL) { + return KNOT_ENOMEM; + } + + *ret = val->data; + return KNOT_EOK; +} + +static int del(knot_txn_t *txn, knot_val_t *key) +{ + return hattrie_del((hattrie_t *)txn->db, key->data, key->len); +} + +static knot_iter_t *iter_begin(knot_txn_t *txn, unsigned flags) +{ + return hattrie_iter_begin((hattrie_t *)txn->db, (flags & KNOT_NAMEDB_SORTED)); +} + +static knot_iter_t *iter_next(knot_iter_t *iter) +{ + hattrie_iter_next((hattrie_iter_t *)iter); + if (hattrie_iter_finished((hattrie_iter_t *)iter)) { + hattrie_iter_free((hattrie_iter_t *)iter); + return NULL; + } + + return iter; +} + +static int iter_key(knot_iter_t *iter, knot_val_t *val) +{ + val->data = (void *)hattrie_iter_key((hattrie_iter_t *)iter, &val->len); + if (val->data == NULL) { + return KNOT_ENOENT; + } + + return KNOT_EOK; +} + +static int iter_val(knot_iter_t *iter, knot_val_t *val) +{ + value_t *ret = hattrie_iter_val((hattrie_iter_t *)iter); + if (ret == NULL) { + return KNOT_ENOENT; + } + + val->data = *ret; + val->len = sizeof(value_t); + return KNOT_EOK; +} + +static void iter_finish(knot_iter_t *iter) +{ + hattrie_iter_free((hattrie_iter_t *)iter); +} + +struct namedb_api *namedb_trie_api(void) +{ + static struct namedb_api api = { + "hattrie", + init, deinit, + txn_begin, txn_commit, txn_abort, + count, find, insert, del, + iter_begin, iter_next, iter_key, iter_val, iter_finish + }; + + return &api; +} diff --git a/src/common/namedb/namedb_trie.h b/src/common/namedb/namedb_trie.h new file mode 100644 index 0000000000000000000000000000000000000000..048f299d2d01afb4fa7176da6a2bf6ae62316544 --- /dev/null +++ b/src/common/namedb/namedb_trie.h @@ -0,0 +1,21 @@ +/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "common/namedb/namedb.h" + +struct namedb_api *namedb_trie_api(void); diff --git a/src/knot/conf/cf-parse.y b/src/knot/conf/cf-parse.y index 9010d4fc52d023a52bedba4dc324043b1bd76595..0db4e115d1d034b9f36d13cfbe951057f70b0151 100644 --- a/src/knot/conf/cf-parse.y +++ b/src/knot/conf/cf-parse.y @@ -307,7 +307,7 @@ static void query_module_create(void *scanner, const char *name, const char *par static int conf_key_exists(void *scanner, char *item) { /* Find existing node in keys. */ - knot_dname_t *sample = knot_dname_from_str(item); + knot_dname_t *sample = knot_dname_from_str_alloc(item); knot_dname_to_lower(sample); conf_key_t* r = 0; WALK_LIST (r, new_config->keys) { @@ -328,7 +328,7 @@ static int conf_key_add(void *scanner, knot_tsig_key_t **key, char *item) *key = 0; /* Find in keys */ - knot_dname_t *sample = knot_dname_from_str(item); + knot_dname_t *sample = knot_dname_from_str_alloc(item); knot_dname_to_lower(sample); conf_key_t* r = 0; @@ -376,7 +376,7 @@ static void conf_zone_start(void *scanner, char *name) { /* Check domain name. */ knot_dname_t *dn = NULL; if (this_zone->name != NULL) { - dn = knot_dname_from_str(this_zone->name); + dn = knot_dname_from_str_alloc(this_zone->name); } if (dn == NULL) { free(this_zone->name); @@ -679,7 +679,7 @@ keys: } if (fqdn != NULL && !conf_key_exists(scanner, fqdn)) { - knot_dname_t *dname = knot_dname_from_str(fqdn); + knot_dname_t *dname = knot_dname_from_str_alloc(fqdn); if (!dname) { cf_error(scanner, "key name '%s' not in valid domain name format", fqdn); diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c index f77fb678aa9b5cf12e2747b06102f9d459d36495..8c7fd7fceb6d27208c8bfa45d584822d34fae208 100644 --- a/src/knot/conf/conf.c +++ b/src/knot/conf/conf.c @@ -33,6 +33,7 @@ #include "knot/knot.h" #include "knot/ctl/remote.h" #include "knot/nameserver/internet.h" +#include "knot/zone/timers.h" /* * Defaults. @@ -724,6 +725,10 @@ void conf_truncate(conf_t *conf, int unload_hooks) /* Free remote control iface. */ conf_free_iface(conf->ctl.iface); + + /* Close timers db. */ + close_timers_db(conf->timers_db); + conf->timers_db = NULL; } void conf_free(conf_t *conf) @@ -776,6 +781,12 @@ int conf_open(const char* path) return ret; } + /* Open zone timers db. */ + nconf->timers_db = open_timers_db(nconf->storage); + if (nconf->timers_db == NULL) { + log_warning("cannot open timers db\n"); + } + /* Replace current config. */ conf_t **current_config = &s_config; conf_t *oldconf = rcu_xchg_pointer(current_config, nconf); @@ -795,10 +806,11 @@ int conf_open(const char* path) /* Update hooks. */ conf_update_hooks(nconf); - + /* Free old config. */ conf_free(oldconf); } + return KNOT_EOK; } diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h index 97afd9595804e1e39bbd9a43a7420c95a06fac24..e30c033aa10a6c9c8fce99a78216f18227ed69f7 100644 --- a/src/knot/conf/conf.h +++ b/src/knot/conf/conf.h @@ -38,6 +38,7 @@ #include "libknot/dnssec/key.h" #include "libknot/dnssec/policy.h" #include "common-knot/lists.h" +#include "common/namedb/namedb.h" #include "common/log.h" #include "knot/updates/acl.h" #include "common-knot/sockaddr.h" @@ -268,6 +269,11 @@ typedef struct conf_t { int serial_policy; /*!< Serial policy when updating zone. */ struct query_plan *query_plan; list_t query_modules; + + /* + * Zone timers db + */ + knot_namedb_t *timers_db; /* * Remote control interface. diff --git a/src/knot/ctl/knotc_main.c b/src/knot/ctl/knotc_main.c index d882106d7ddfc1d0c55650f56f26ed2af7d7e23c..122a31e0f0ccd421b40d9f4cb59c46e5ef7e9191 100644 --- a/src/knot/ctl/knotc_main.c +++ b/src/knot/ctl/knotc_main.c @@ -42,8 +42,7 @@ enum knotc_flag_t { F_NULL = 0 << 0, F_FORCE = 1 << 0, F_VERBOSE = 1 << 1, - F_INTERACTIVE = 1 << 2, - F_NOCONF = 1 << 3, + F_NOCONF = 1 << 2 }; /*! \brief Check if flag is present. */ @@ -79,9 +78,9 @@ static int cmd_signzone(int argc, char *argv[], unsigned flags); /*! \brief Table of remote commands. */ knot_cmd_t knot_cmd_tbl[] = { {&cmd_stop, 0, "stop", "", "\t\tStop server."}, - {&cmd_reload, 0, "reload", "", "\tReload configuration and changed zones."}, + {&cmd_reload, 0, "reload", "<zone>", "\tReload configuration and changed zones."}, {&cmd_refresh, 0, "refresh", "<zone>", "\tRefresh slave zone (all if not specified). Flag '-f' forces retransfer."}, - {&cmd_flush, 0, "flush", "", "\t\tFlush journal and update zone files."}, + {&cmd_flush, 0, "flush", "<zone>", "\t\tFlush journal and update zone file (all if not specified)."}, {&cmd_status, 0, "status", "", "\tCheck if server is running."}, {&cmd_zonestatus, 0, "zonestatus", "", "\tShow status of configured zones."}, {&cmd_checkconf, 1, "checkconf", "", "\tCheck current server configuration."}, @@ -104,7 +103,6 @@ void help(void) " -f, --force \tForce operation - override some checks.\n" " -v, --verbose \tVerbose mode - additional runtime information.\n" " -V, --version \tPrint %s server version.\n" - " -i, --interactive \tInteractive mode (do not daemonize).\n" " -h, --help \tPrint help and usage.\n", RUN_DIR "/knot.sock", PACKAGE_NAME); printf("\nActions:\n"); @@ -434,7 +432,6 @@ int main(int argc, char **argv) {"force", no_argument, 0, 'f'}, {"config", required_argument, 0, 'c'}, {"verbose", no_argument, 0, 'v'}, - {"interactive", no_argument, 0, 'i'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"server", required_argument, 0, 's' }, @@ -444,7 +441,7 @@ int main(int argc, char **argv) {0, 0, 0, 0} }; - while ((c = getopt_long(argc, argv, "s:p:y:k:fc:viVh", opts, &li)) != -1) { + while ((c = getopt_long(argc, argv, "s:p:y:k:fc:vVh", opts, &li)) != -1) { switch (c) { case 's': r_addr = optarg; @@ -472,9 +469,6 @@ int main(int argc, char **argv) case 'v': flags |= F_VERBOSE; break; - case 'i': - flags |= F_INTERACTIVE; - break; case 'c': config_fn = optarg; break; @@ -662,7 +656,7 @@ static int cmd_checkconf(int argc, char *argv[], unsigned flags) static bool fetch_zone(int argc, char *argv[], conf_zone_t *zone) { /* Convert zone name to dname */ - knot_dname_t *zone_name = knot_dname_from_str(zone->name); + knot_dname_t *zone_name = knot_dname_from_str_alloc(zone->name); if (zone_name == NULL) { return false; } @@ -673,7 +667,7 @@ static bool fetch_zone(int argc, char *argv[], conf_zone_t *zone) int i = 0; while (!found && i < argc) { /* Convert the argument to dname */ - knot_dname_t *arg_name = knot_dname_from_str(argv[i]); + knot_dname_t *arg_name = knot_dname_from_str_alloc(argv[i]); if (arg_name != NULL) { (void)knot_dname_to_lower(arg_name); diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c index db5cddd1a5b585524734ec98ea7dbb8164185d7f..067f39bfb8ee68faaf9f40f5eb571848bc1f9a7c 100644 --- a/src/knot/ctl/remote.c +++ b/src/knot/ctl/remote.c @@ -32,6 +32,7 @@ #include "libknot/rrtype/soa.h" #include "libknot/dnssec/random.h" #include "libknot/packet/wire.h" +#include "knot/zone/timers.h" #include "knot/dnssec/zone-sign.h" #include "knot/dnssec/zone-nsec.h" @@ -607,7 +608,7 @@ static void log_command(const char *cmd, const remote_cmdargs_t* args) uint16_t rr_count = rr->rrs.rr_count; for (uint16_t j = 0; j < rr_count; j++) { const knot_dname_t *dn = knot_ns_name(&rr->rrs, j); - char *name = knot_dname_to_str(dn); + char *name = knot_dname_to_str_alloc(dn); int ret = snprintf(params, rest, " %s", name); free(name); @@ -637,7 +638,7 @@ int remote_answer(int sock, server_t *s, knot_pkt_t *pkt) return KNOT_EMALF; } - knot_dname_t *realm = knot_dname_from_str(KNOT_CTL_REALM); + knot_dname_t *realm = knot_dname_from_str_alloc(KNOT_CTL_REALM); if (!knot_dname_is_sub(qname, realm) != 0) { dbg_server("remote: qname != *%s\n", KNOT_CTL_REALM_EXT); knot_dname_free(&realm, NULL); @@ -899,7 +900,7 @@ knot_pkt_t* remote_query(const char *query, const knot_tsig_key_t *key) /* Question section. */ char *qname = strcdup(query, KNOT_CTL_REALM_EXT); - knot_dname_t *dname = knot_dname_from_str(qname); + knot_dname_t *dname = knot_dname_from_str_alloc(qname); free(qname); if (!dname) { knot_pkt_free(&pkt); @@ -944,7 +945,7 @@ int remote_build_rr(knot_rrset_t *rr, const char *k, uint16_t t) } /* Assert K is FQDN. */ - knot_dname_t *key = knot_dname_from_str(k); + knot_dname_t *key = knot_dname_from_str_alloc(k); if (key == NULL) { return KNOT_ENOMEM; } @@ -993,7 +994,7 @@ int remote_create_ns(knot_rrset_t *rr, const char *d) } /* Create dname. */ - knot_dname_t *dn = knot_dname_from_str(d); + knot_dname_t *dn = knot_dname_from_str_alloc(d); if (!dn) { return KNOT_ERROR; } diff --git a/src/knot/dnssec/nsec3-chain.c b/src/knot/dnssec/nsec3-chain.c index c486a1508c32a89901c7e175baaed21241c6ca77..685ee2998a7150e3e9446674b46793a6414ce264 100644 --- a/src/knot/dnssec/nsec3-chain.c +++ b/src/knot/dnssec/nsec3-chain.c @@ -287,7 +287,7 @@ static zone_node_t *create_nsec3_node(knot_dname_t *owner, if (!new_node) { return NULL; } - + node_set_parent(new_node, apex_node); knot_rrset_t nsec3_rrset; @@ -385,7 +385,7 @@ static int connect_nsec3_nodes(zone_node_t *a, zone_node_t *b, assert(raw_length == knot_nsec3_hash_length(algorithm)); knot_dname_to_lower(b->owner); - uint8_t *b32_hash = (uint8_t *)knot_dname_to_str(b->owner); + uint8_t *b32_hash = (uint8_t *)knot_dname_to_str_alloc(b->owner); size_t b32_length = knot_nsec3_hash_b32_length(algorithm); if (!b32_hash) { return KNOT_ENOMEM; diff --git a/src/knot/main.c b/src/knot/main.c index 720d0ed4796ab53bf9122dd60abebfa28f03eb7f..2dc5e3f55613ac3a6993f0c30abe159b9f87a927 100644 --- a/src/knot/main.c +++ b/src/knot/main.c @@ -36,6 +36,7 @@ #include "knot/ctl/process.h" #include "knot/ctl/remote.h" #include "knot/conf/conf.h" +#include "knot/zone/timers.h" #include "knot/server/tcp-handler.h" /* Signal flags. */ diff --git a/src/knot/modules/synth_record.c b/src/knot/modules/synth_record.c index 779b3517a11d9d0af2fa122749791612423f14e4..80be22c2a3fa16383e78d5ffb117587964d5551e 100644 --- a/src/knot/modules/synth_record.c +++ b/src/knot/modules/synth_record.c @@ -185,7 +185,7 @@ static knot_dname_t *synth_ptrname(const char *addr_str, synth_template_t *tpl) memcpy(ptrname + written, tpl->zone, zone_len); /* Convert to domain name. */ - return knot_dname_from_str(ptrname); + return knot_dname_from_str_alloc(ptrname); } static int reverse_rr(char *addr_str, synth_template_t *tpl, knot_pkt_t *pkt, knot_rrset_t *rr) @@ -361,7 +361,7 @@ int synth_record_load(struct query_plan *plan, struct query_module *self) /* Parse zone if generating reverse record. */ if (tpl->type == SYNTH_REVERSE) { tpl->zone = strtok_r(NULL, " ", &saveptr); - knot_dname_t *check_name = knot_dname_from_str(tpl->zone); + knot_dname_t *check_name = knot_dname_from_str_alloc(tpl->zone); if (check_name == NULL) { MODULE_ERR("invalid zone '%s'", tpl->zone); return KNOT_EMALF; diff --git a/src/knot/nameserver/chaos.c b/src/knot/nameserver/chaos.c index 41822136355eede084f8557e4ed8e47cd2763114..086ca8e7aff258df824dfdfd1f3091f0c1ce0d5e 100644 --- a/src/knot/nameserver/chaos.c +++ b/src/knot/nameserver/chaos.c @@ -26,7 +26,7 @@ */ static const char *get_txt_response_string(const knot_dname_t *qname) { - char *qname_str = knot_dname_to_str(qname); + char *qname_str = knot_dname_to_str_alloc(qname); const char *response = NULL; /* id.server and hostname.bind should have similar meaning */ diff --git a/src/knot/nameserver/internet.c b/src/knot/nameserver/internet.c index d49ad312915300265b2c708c2dd102396bab2dd4..17c13d8e1f589c806ba94603a7306835d1eec81c 100644 --- a/src/knot/nameserver/internet.c +++ b/src/knot/nameserver/internet.c @@ -416,8 +416,8 @@ static int follow_cname(knot_pkt_t *pkt, uint16_t rrtype, struct query_data *qda qdata->name = knot_cname_name(&cname_rr.rrs); #ifdef KNOT_NS_DEBUG - char *cname_str = knot_dname_to_str(cname_node->owner); - char *target_str = knot_dname_to_str(qdata->name); + char *cname_str = knot_dname_to_str_alloc(cname_node->owner); + char *target_str = knot_dname_to_str_alloc(qdata->name); dbg_ns("%s: FOLLOW '%s' -> '%s'\n", __func__, cname_str, target_str); free(cname_str); free(target_str); diff --git a/src/knot/nameserver/notify.c b/src/knot/nameserver/notify.c index 9726acea2a94743acc8149fbacb4af2bbc3253ec..502b80de35d4ca03563d38962a8bc0dd21398498 100644 --- a/src/knot/nameserver/notify.c +++ b/src/knot/nameserver/notify.c @@ -24,6 +24,7 @@ #include "libknot/rrset.h" #include "libknot/consts.h" #include "knot/zone/zonedb.h" +#include "knot/zone/timers.h" #include "libknot/common.h" #include "libknot/packet/wire.h" #include "knot/updates/acl.h" @@ -99,6 +100,10 @@ int notify_process_query(knot_pkt_t *pkt, struct query_data *qdata) /* Incoming NOTIFY expires REFRESH timer and renews EXPIRE timer. */ zone_t *zone = (zone_t *)qdata->zone; zone_events_schedule(zone, ZONE_EVENT_REFRESH, ZONE_EVENT_NOW); + int ret = write_zone_timers(conf()->timers_db, zone); + if (ret != KNOT_EOK) { + return NS_PROC_FAIL; + } return NS_PROC_DONE; } diff --git a/src/knot/nameserver/nsec_proofs.c b/src/knot/nameserver/nsec_proofs.c index abf8546a364f40e913c530d8121760b4908eb533..1cb76edbb3bb51b4ce799d2919251d355b997cbf 100644 --- a/src/knot/nameserver/nsec_proofs.c +++ b/src/knot/nameserver/nsec_proofs.c @@ -107,7 +107,7 @@ static int ns_put_covering_nsec3(const zone_contents_t *zone, } dbg_ns_exec_verb( - char *name = knot_dname_to_str(prev->owner); + char *name = knot_dname_to_str_alloc(prev->owner); dbg_ns_verb("Covering NSEC3 node: %s\n", name); free(name); ); @@ -155,7 +155,7 @@ static int ns_put_nsec3_closest_encloser_proof( if (zone_contents_nsec3params(zone) == NULL) { dbg_ns_exec_verb( - char *name = knot_dname_to_str(zone->apex->owner); + char *name = knot_dname_to_str_alloc(zone->apex->owner); dbg_ns_verb("No NSEC3PARAM found in zone %s.\n", name); free(name); ); @@ -163,7 +163,7 @@ dbg_ns_exec_verb( } dbg_ns_exec_detail( - char *name = knot_dname_to_str((*closest_encloser)->owner); + char *name = knot_dname_to_str_alloc((*closest_encloser)->owner); dbg_ns_detail("Closest encloser: %s\n", name); free(name); ); @@ -186,14 +186,14 @@ dbg_ns_exec_detail( assert(nsec3_node != NULL); dbg_ns_exec_verb( - char *name = knot_dname_to_str(nsec3_node->owner); + char *name = knot_dname_to_str_alloc(nsec3_node->owner); dbg_ns_verb("NSEC3 node: %s\n", name); free(name); - name = knot_dname_to_str((*closest_encloser)->owner); + name = knot_dname_to_str_alloc((*closest_encloser)->owner); dbg_ns_verb("Closest provable encloser: %s\n", name); free(name); if (next_closer != NULL) { - name = knot_dname_to_str(next_closer); + name = knot_dname_to_str_alloc(next_closer); dbg_ns_verb("Next closer name: %s\n", name); free(name); } else { @@ -218,7 +218,7 @@ dbg_ns_exec_verb( return KNOT_ERROR; /*servfail */ } dbg_ns_exec_verb( - char *name = knot_dname_to_str(new_next_closer); + char *name = knot_dname_to_str_alloc(new_next_closer); dbg_ns_verb("Next closer name: %s\n", name); free(name); ); @@ -244,7 +244,7 @@ static knot_dname_t *ns_wildcard_child_name(const knot_dname_t *name) { assert(name != NULL); - knot_dname_t *wildcard = knot_dname_from_str("*"); + knot_dname_t *wildcard = knot_dname_from_str_alloc("*"); if (wildcard == NULL) { return NULL; } @@ -254,7 +254,7 @@ static knot_dname_t *ns_wildcard_child_name(const knot_dname_t *name) return NULL; dbg_ns_exec_verb( - char *name = knot_dname_to_str(wildcard); + char *name = knot_dname_to_str_alloc(wildcard); dbg_ns_verb("Wildcard: %s\n", name); free(name); ); @@ -386,7 +386,7 @@ static int ns_put_nsec3_wildcard(const zone_contents_t *zone, return KNOT_ERROR; /* servfail */ } dbg_ns_exec_verb( - char *name = knot_dname_to_str(next_closer); + char *name = knot_dname_to_str_alloc(next_closer); dbg_ns_verb("Next closer name: %s\n", name); free(name); ); @@ -480,7 +480,7 @@ static int ns_put_nsec_nxdomain(const knot_dname_t *qname, } dbg_ns_exec_verb( - char *name = knot_dname_to_str(previous->owner); + char *name = knot_dname_to_str_alloc(previous->owner); dbg_ns_verb("Previous node: %s\n", name); free(name); ); @@ -517,7 +517,7 @@ dbg_ns_exec_verb( while (knot_dname_cmp(prev_new->owner, wildcard) > 0) { dbg_ns_exec_verb( - char *name = knot_dname_to_str(prev_new->owner); + char *name = knot_dname_to_str_alloc(prev_new->owner); dbg_ns_verb("Previous node: %s\n", name); free(name); ); @@ -527,7 +527,7 @@ dbg_ns_exec_verb( assert(knot_dname_cmp(prev_new->owner, wildcard) < 0); dbg_ns_exec_verb( - char *name = knot_dname_to_str(prev_new->owner); + char *name = knot_dname_to_str_alloc(prev_new->owner); dbg_ns_verb("Previous node: %s\n", name); free(name); ); diff --git a/src/knot/nameserver/update.c b/src/knot/nameserver/update.c index b8212a8bf8ad792fadc583d84304ce2f1663389d..44f8acec7ea5aad74c8b71177b3c974f5ef99fde 100644 --- a/src/knot/nameserver/update.c +++ b/src/knot/nameserver/update.c @@ -26,7 +26,7 @@ #include "libknot/descriptor.h" #include "libknot/tsig-op.h" #include "knot/zone/zone.h" -#include "knot/zone/events.h" +#include "knot/zone/events/events.h" #include "knot/server/tcp-handler.h" #include "knot/server/udp-handler.h" #include "knot/nameserver/requestor.h" diff --git a/src/knot/server/server.h b/src/knot/server/server.h index f151e55fbda0e9f7e4670213977b8cb73b3ed323..342666fc491840d836d7a32b78a944c7be819dba 100644 --- a/src/knot/server/server.h +++ b/src/knot/server/server.h @@ -21,13 +21,7 @@ * \brief Core server functions. * * Contains the main high-level server structure (server_t) and interface - * to functions taking care of proper initialization of the server and clean-up - * when terminated. - * - * As of now, the server supports only one zone file and only in a special - * format. - * - * \see zone-parser.h + * to functions taking care of initialization of the server and clean-up. * * \addtogroup server * @{ @@ -59,11 +53,6 @@ typedef struct iohandler { unsigned *thread_id; /*!< Thread identifier. */ } iohandler_t; -/*! \brief Round-robin mechanism of switching. - */ -#define get_next_rr(current, count) \ - (((current) + 1) % (count)) - /*! \brief Server state flags. */ typedef enum { diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c index a7a18251a64797d494e3d3372b24c11682718a10..9f1da742f29f047f316b2ba62a8f28b545e2043b 100644 --- a/src/knot/server/udp-handler.c +++ b/src/knot/server/udp-handler.c @@ -489,10 +489,8 @@ int udp_master(dthread_t *thread) { unsigned cpu = dt_online_cpus(); if (cpu > 1) { - unsigned cpu_mask[2]; - cpu_mask[0] = dt_get_id(thread) % cpu; - cpu_mask[1] = (cpu_mask[0] + 2) % cpu; - dt_setaffinity(thread, cpu_mask, 2); + unsigned cpu_mask = (dt_get_id(thread) % cpu); + dt_setaffinity(thread, &cpu_mask, 1); } /* Drop all capabilities on all workers. */ diff --git a/src/knot/updates/ddns.c b/src/knot/updates/ddns.c index 9a4158ade629db75a427d4be0928f39e36b77930..494d00bbf6c61be155d381c6928b1e7f7f2f9fe7 100644 --- a/src/knot/updates/ddns.c +++ b/src/knot/updates/ddns.c @@ -436,7 +436,7 @@ static bool singleton_replaced(changeset_t *changeset, if (!should_replace(rr)) { return false; } - + zone_node_t *n = zone_contents_find_node_for_rr(changeset->add, rr); if (n == NULL) { return false; @@ -446,12 +446,12 @@ static bool singleton_replaced(changeset_t *changeset, if (rrs == NULL) { return false; } - + // Replace singleton RR. knot_rdataset_clear(rrs, NULL); node_remove_rdataset(n, rr->type); node_add_rrset(n, rr, NULL); - + return true; } @@ -544,7 +544,7 @@ static int process_add_nsec3param(const zone_node_t *node, { if (node == NULL || !node_rrtype_exists(node, KNOT_RRTYPE_SOA)) { // Ignore non-apex additions - char *owner = knot_dname_to_str(rr->owner); + char *owner = knot_dname_to_str_alloc(rr->owner); log_warning("DDNS, refusing to add NSEC3PARAM to non-apex " "node '%s'", owner); free(owner); @@ -555,7 +555,7 @@ static int process_add_nsec3param(const zone_node_t *node, return add_rr_to_chgset(rr, changeset, NULL); } - char *owner = knot_dname_to_str(rr->owner); + char *owner = knot_dname_to_str_alloc(rr->owner); log_warning("DDNS, refusing to add second NSEC3PARAM to node '%s'", owner); free(owner); @@ -918,7 +918,7 @@ int ddns_process_update(const zone_t *zone, const knot_pkt_t *query, } return KNOT_EINVAL; } - + changeset_t *changeset = update->change; if (changeset->soa_from == NULL) { diff --git a/src/knot/zone/contents.c b/src/knot/zone/contents.c index 81d92fc1f69ff825ea253e59506170b604b502d4..6d30d8072e1a73bb8fcf872a48dc58c6ae5ab797 100644 --- a/src/knot/zone/contents.c +++ b/src/knot/zone/contents.c @@ -744,7 +744,7 @@ int zone_contents_remove_node(zone_contents_t *contents, const knot_dname_t *own } dbg_zone_exec_verb( - char *name = knot_dname_to_str(owner); + char *name = knot_dname_to_str_alloc(owner); dbg_zone_verb("Removing zone node: %s\n", name); free(name); ); @@ -801,8 +801,8 @@ int zone_contents_find_dname(const zone_contents_t *zone, } dbg_zone_exec_verb( - char *name_str = knot_dname_to_str(name); - char *zone_str = knot_dname_to_str(zone->apex->owner); + char *name_str = knot_dname_to_str_alloc(name); + char *zone_str = knot_dname_to_str_alloc(zone->apex->owner); dbg_zone_verb("Searching for name %s in zone %s...\n", name_str, zone_str); free(name_str); @@ -818,10 +818,10 @@ dbg_zone_exec_verb( *previous = prev; dbg_zone_exec_detail( - char *name_str = (*node) ? knot_dname_to_str((*node)->owner) + char *name_str = (*node) ? knot_dname_to_str_alloc((*node)->owner) : "(nil)"; char *name_str2 = (*previous != NULL) - ? knot_dname_to_str((*previous)->owner) + ? knot_dname_to_str_alloc((*previous)->owner) : "(nil)"; dbg_zone_detail("Search function returned %d, node %s (%p) and prev: %s (%p)\n", exact_match, name_str, *node, name_str2, *previous); @@ -933,7 +933,7 @@ int zone_contents_find_nsec3_for_name(const zone_contents_t *zone, } dbg_zone_exec_verb( - char *n = knot_dname_to_str(nsec3_name); + char *n = knot_dname_to_str_alloc(nsec3_name); dbg_zone_verb("NSEC3 node name: %s.\n", n); free(n); ); @@ -949,7 +949,7 @@ dbg_zone_exec_verb( dbg_zone_exec_detail( if (found) { - char *n = knot_dname_to_str(found->owner); + char *n = knot_dname_to_str_alloc(found->owner); dbg_zone_detail("Found NSEC3 node: %s.\n", n); free(n); } else { @@ -958,7 +958,7 @@ dbg_zone_exec_detail( if (prev) { assert(prev->owner); - char *n = knot_dname_to_str(prev->owner); + char *n = knot_dname_to_str_alloc(prev->owner); dbg_zone_detail("Found previous NSEC3 node: %s.\n", n); free(n); } else { diff --git a/src/knot/zone/events/events.c b/src/knot/zone/events/events.c new file mode 100644 index 0000000000000000000000000000000000000000..0a6821002a72ed66a9991c39b4db7979b4c72423 --- /dev/null +++ b/src/knot/zone/events/events.c @@ -0,0 +1,411 @@ +/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <time.h> + +#include "common-knot/evsched.h" +#include "knot/server/server.h" +#include "knot/worker/pool.h" +#include "knot/zone/zone.h" +#include "knot/zone/events/events.h" +#include "knot/zone/events/handlers.h" +#include "knot/zone/events/replan.h" + +/* ------------------------- internal timers -------------------------------- */ + +#define ZONE_EVENT_IMMEDIATE 1 /* Fast-track to worker queue. */ + +/* -- internal API ---------------------------------------------------------- */ + +typedef int (*zone_event_cb)(zone_t *zone); + +typedef struct event_info_t { + zone_event_type_t type; + const zone_event_cb callback; + const char *name; +} event_info_t; + +static const event_info_t EVENT_INFO[] = { + { ZONE_EVENT_RELOAD, event_reload, "reload" }, + { ZONE_EVENT_REFRESH, event_refresh, "refresh" }, + { ZONE_EVENT_XFER, event_xfer, "transfer" }, + { ZONE_EVENT_UPDATE, event_update, "update" }, + { ZONE_EVENT_EXPIRE, event_expire, "expiration" }, + { ZONE_EVENT_FLUSH, event_flush, "journal flush" }, + { ZONE_EVENT_NOTIFY, event_notify, "notify" }, + { ZONE_EVENT_DNSSEC, event_dnssec, "DNSSEC resign" }, + { 0 } +}; + +static const event_info_t *get_event_info(zone_event_type_t type) +{ + const event_info_t *info; + for (info = EVENT_INFO; info->callback != NULL; info++) { + if (info->type == type) { + return info; + } + } + + assert(0); + return NULL; +} + +static bool valid_event(zone_event_type_t type) +{ + return (type > ZONE_EVENT_INVALID && type < ZONE_EVENT_COUNT); +} + +/*! \brief Return remaining time to planned event (seconds). */ +static time_t time_until(time_t planned) +{ + time_t now = time(NULL); + return now < planned ? (planned - now) : 0; +} + +/*! + * \brief Find next scheduled zone event. + * + * \param events Zone events. + * + * \return Zone event type, or ZONE_EVENT_INVALID if no event is scheduled. + */ +static zone_event_type_t get_next_event(zone_events_t *events) +{ + if (!events) { + return ZONE_EVENT_INVALID; + } + + zone_event_type_t next_type = ZONE_EVENT_INVALID; + time_t next = 0; + + for (int i = 0; i < ZONE_EVENT_COUNT; i++) { + time_t current = events->time[i]; + if (current == 0) { + continue; + } + + if (next == 0 || current < next) { + next = current; + next_type = i; + } + } + + return next_type; +} + +/*! + * \brief Set time of a given event type. + */ +static void event_set_time(zone_events_t *events, zone_event_type_t type, time_t time) +{ + assert(events); + assert(valid_event(type)); + + events->time[type] = time; +} + +/*! + * \brief Get time of a given event type. + */ +static time_t event_get_time(zone_events_t *events, zone_event_type_t type) +{ + assert(events); + assert(valid_event(type)); + + return events->time[type]; +} + +/*! + * \brief Cancel scheduled item, schedule first enqueued item. + * + * The events mutex must be locked when calling this function. + */ +static void reschedule(zone_events_t *events) +{ + assert(events); + assert(pthread_mutex_trylock(&events->mx) == EBUSY); + + if (!events->event || events->running || events->frozen) { + return; + } + + zone_event_type_t type = get_next_event(events); + if (!valid_event(type)) { + return; + } + + time_t diff = time_until(event_get_time(events, type)); + + evsched_schedule(events->event, diff * 1000); +} + +/* -- callbacks control ----------------------------------------------------- */ + +/*! + * \brief Zone event wrapper, expected to be called from a worker thread. + * + * 1. Takes the next planned event. + * 2. Resets the event's scheduled time. + * 3. Perform the event's callback. + * 4. Schedule next event planned event. + */ +static void event_wrap(task_t *task) +{ + assert(task); + assert(task->ctx); + + zone_t *zone = task->ctx; + zone_events_t *events = &zone->events; + + pthread_mutex_lock(&events->mx); + zone_event_type_t type = get_next_event(events); + if (!valid_event(type)) { + events->running = false; + pthread_mutex_unlock(&events->mx); + return; + } + event_set_time(events, type, 0); + pthread_mutex_unlock(&events->mx); + + const event_info_t *info = get_event_info(type); + int result = info->callback(zone); + if (result != KNOT_EOK) { + log_zone_error(zone->name, "zone %s failed (%s)", info->name, + knot_strerror(result)); + } + + pthread_mutex_lock(&events->mx); + events->running = false; + reschedule(events); + pthread_mutex_unlock(&events->mx); +} + +/*! + * \brief Called by scheduler thread if the event occurs. + */ +static int event_dispatch(event_t *event) +{ + assert(event); + assert(event->data); + + zone_events_t *events = event->data; + + pthread_mutex_lock(&events->mx); + if (!events->running && !events->frozen) { + events->running = true; + worker_pool_assign(events->pool, &events->task); + } + pthread_mutex_unlock(&events->mx); + + return KNOT_EOK; +} + +/* -- public API ------------------------------------------------------------ */ + +int zone_events_init(zone_t *zone) +{ + if (!zone) { + return KNOT_EINVAL; + } + + zone_events_t *events = &zone->events; + + memset(&zone->events, 0, sizeof(zone->events)); + pthread_mutex_init(&events->mx, NULL); + events->task.ctx = zone; + events->task.run = event_wrap; + + return KNOT_EOK; +} + +int zone_events_setup(zone_t *zone, worker_pool_t *workers, evsched_t *scheduler) +{ + if (!zone || !workers || !scheduler) { + return KNOT_EINVAL; + } + + event_t *event; + event = evsched_event_create(scheduler, event_dispatch, &zone->events); + if (!event) { + return KNOT_ENOMEM; + } + + zone->events.event = event; + zone->events.pool = workers; + + return KNOT_EOK; +} + +void zone_events_deinit(zone_t *zone) +{ + if (!zone) { + return; + } + + evsched_cancel(zone->events.event); + evsched_event_free(zone->events.event); + + pthread_mutex_destroy(&zone->events.mx); + + memset(&zone->events, 0, sizeof(zone->events)); +} + +void zone_events_schedule_at(zone_t *zone, zone_event_type_t type, time_t time) +{ + if (!zone || !valid_event(type)) { + return; + } + + zone_events_t *events = &zone->events; + + pthread_mutex_lock(&events->mx); + event_set_time(events, type, time); + reschedule(events); + pthread_mutex_unlock(&events->mx); +} + +void zone_events_enqueue(zone_t *zone, zone_event_type_t type) +{ + if (!zone || !valid_event(type)) { + return; + } + + zone_events_t *events = &zone->events; + + pthread_mutex_lock(&events->mx); + + /* Bypass scheduler if no event is running. */ + if (!events->running && !events->frozen) { + events->running = true; + event_set_time(events, type, ZONE_EVENT_IMMEDIATE); + worker_pool_assign(events->pool, &events->task); + pthread_mutex_unlock(&events->mx); + return; + } + + pthread_mutex_unlock(&events->mx); + + /* Execute as soon as possible. */ + zone_events_schedule(zone, type, ZONE_EVENT_NOW); +} + +void zone_events_schedule(zone_t *zone, zone_event_type_t type, unsigned dt) +{ + time_t abstime = time(NULL) + dt; + return zone_events_schedule_at(zone, type, abstime); +} + +void zone_events_cancel(zone_t *zone, zone_event_type_t type) +{ + zone_events_schedule_at(zone, type, 0); +} + +void zone_events_freeze(zone_t *zone) +{ + if (!zone) { + return; + } + + zone_events_t *events = &zone->events; + + /* Prevent new events being enqueued. */ + pthread_mutex_lock(&events->mx); + events->frozen = true; + pthread_mutex_unlock(&events->mx); + + /* Cancel current event. */ + evsched_cancel(events->event); +} + +void zone_events_start(zone_t *zone) +{ + if (!zone) { + return; + } + + pthread_mutex_lock(&zone->events.mx); + reschedule(&zone->events); + pthread_mutex_unlock(&zone->events.mx); +} + +time_t zone_events_get_time(const struct zone_t *zone, zone_event_type_t type) +{ + if (zone == NULL) { + return KNOT_EINVAL; + } + + time_t event_time = KNOT_ENOENT; + zone_events_t *events = (zone_events_t *)&zone->events; + + pthread_mutex_lock(&events->mx); + + /* Get next valid event. */ + if (valid_event(type)) { + event_time = event_get_time(events, type); + } + + pthread_mutex_unlock(&events->mx); + + return event_time; +} + +const char *zone_events_get_name(zone_event_type_t type) +{ + /* Get information about the event and time. */ + const event_info_t *info = get_event_info(type); + if (info == NULL) { + return NULL; + } + + return info->name; +} + +time_t zone_events_get_next(const struct zone_t *zone, zone_event_type_t *type) +{ + if (zone == NULL || type == NULL) { + return KNOT_EINVAL; + } + + time_t next_time = KNOT_ENOENT; + zone_events_t *events = (zone_events_t *)&zone->events; + + pthread_mutex_lock(&events->mx); + + /* Get time of next valid event. */ + *type = get_next_event(events); + if (valid_event(*type)) { + next_time = event_get_time(events, *type); + } else { + *type = ZONE_EVENT_INVALID; + } + + pthread_mutex_unlock(&events->mx); + + return next_time; +} + +void zone_events_update(zone_t *zone, const zone_t *old_zone) +{ + replan_events(zone, old_zone); +} + +void zone_events_replan_ddns(struct zone_t *zone, const struct zone_t *old_zone) +{ + if (old_zone) { + replan_update(zone, (zone_t *)old_zone); + } +} diff --git a/src/knot/zone/events.h b/src/knot/zone/events/events.h similarity index 100% rename from src/knot/zone/events.h rename to src/knot/zone/events/events.h diff --git a/src/knot/zone/events.c b/src/knot/zone/events/handlers.c similarity index 54% rename from src/knot/zone/events.c rename to src/knot/zone/events/handlers.c index 0252eeb77ae27fc9c9f936bc31c6ba0ae14abc88..61d45d178d5bcb4a263a30060bf6fe3d2223d206 100644 --- a/src/knot/zone/events.c +++ b/src/knot/zone/events/handlers.c @@ -14,26 +14,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <assert.h> -#include <time.h> - -#include "common-knot/evsched.h" +#include "libknot/rrtype/soa.h" +#include "libknot/dnssec/random.h" #include "common-knot/trim.h" -#include "common/mem.h" #include "common/mempool.h" -#include "knot/server/server.h" #include "knot/server/udp-handler.h" #include "knot/server/tcp-handler.h" #include "knot/updates/changesets.h" #include "knot/dnssec/zone-events.h" -#include "knot/worker/pool.h" -#include "knot/zone/events.h" -#include "knot/zone/zone.h" +#include "knot/zone/timers.h" #include "knot/zone/zone-load.h" #include "knot/zone/zonefile.h" +#include "knot/zone/events/events.h" +#include "knot/zone/events/handlers.h" #include "knot/updates/apply.h" -#include "libknot/rrtype/soa.h" -#include "libknot/dnssec/random.h" #include "knot/nameserver/internet.h" #include "knot/nameserver/update.h" #include "knot/nameserver/notify.h" @@ -41,26 +35,9 @@ #include "knot/nameserver/tsig_ctx.h" #include "knot/nameserver/process_answer.h" -/* ------------------------- internal timers -------------------------------- */ - -#define ZONE_EVENT_IMMEDIATE 1 /* Fast-track to worker queue. */ - -/* ------------------------- bootstrap timer logic -------------------------- */ - #define BOOTSTRAP_RETRY (30) /*!< Interval between AXFR bootstrap retries. */ #define BOOTSTRAP_MAXTIME (24*60*60) /*!< Maximum AXFR retry cap of 24 hours. */ -/*! \brief Progressive bootstrap retry timer. */ -static uint32_t bootstrap_next(uint32_t timer) -{ - timer *= 2; - timer += knot_random_uint32_t() % BOOTSTRAP_RETRY; - if (timer > BOOTSTRAP_MAXTIME) { - timer = BOOTSTRAP_MAXTIME; - } - return timer; -} - /* ------------------------- zone query requesting -------------------------- */ /*! \brief Zone event logging. */ @@ -229,9 +206,7 @@ static uint32_t soa_graceful_expire(const knot_rdataset_t *soa) return knot_soa_expire(soa) + 2 * conf()->max_conn_idle; } -typedef int (*zone_event_cb)(zone_t *zone); - -static int event_reload(zone_t *zone) +int event_reload(zone_t *zone) { assert(zone); @@ -300,14 +275,17 @@ static int event_reload(zone_t *zone) uint32_t current_serial = zone_contents_serial(zone->contents); log_zone_info(zone->name, "loaded, serial %u -> %u", old_serial, current_serial); - return KNOT_EOK; + + return write_zone_timers(conf()->timers_db, zone); fail: zone_contents_deep_free(&contents); return result; } -static int event_refresh(zone_t *zone) +/* -- zone events implementation API ---------------------------------------- */ + +int event_refresh(zone_t *zone) { assert(zone); @@ -343,10 +321,10 @@ static int event_refresh(zone_t *zone) zone_events_cancel(zone, ZONE_EVENT_EXPIRE); } - return KNOT_EOK; + return write_zone_timers(conf()->timers_db, zone); } -static int event_xfer(zone_t *zone) +int event_xfer(zone_t *zone) { assert(zone); @@ -391,7 +369,7 @@ static int event_xfer(zone_t *zone) return KNOT_EOK; } -static int event_update(zone_t *zone) +int event_update(zone_t *zone) { assert(zone); @@ -416,7 +394,7 @@ static int event_update(zone_t *zone) return KNOT_EOK; } -static int event_expire(zone_t *zone) +int event_expire(zone_t *zone) { assert(zone); @@ -436,7 +414,7 @@ static int event_expire(zone_t *zone) return KNOT_EOK; } -static int event_flush(zone_t *zone) +int event_flush(zone_t *zone) { assert(zone); @@ -454,7 +432,7 @@ static int event_flush(zone_t *zone) return zone_flush_journal(zone); } -static int event_notify(zone_t *zone) +int event_notify(zone_t *zone) { assert(zone); @@ -482,7 +460,7 @@ static int event_notify(zone_t *zone) return KNOT_EOK; } -static int event_dnssec(zone_t *zone) +int event_dnssec(zone_t *zone) { assert(zone); @@ -553,497 +531,14 @@ done: #undef ZONE_QUERY_LOG -/* -- Zone event replanning functions --------------------------------------- */ - -/*!< \brief Replans event for new zone according to old zone. */ -static void replan_event(zone_t *zone, const zone_t *old_zone, zone_event_type_t e) -{ - const time_t event_time = zone_events_get_time(old_zone, e); - if (event_time > ZONE_EVENT_NOW) { - zone_events_schedule_at(zone, e, event_time); - } -} - -/*!< \brief Replans events that are dependent on the SOA record. */ -static void replan_soa_events(zone_t *zone, const zone_t *old_zone) -{ - if (!zone_master(zone)) { - // Events only valid for slaves. - return; - } - - if (zone_master(old_zone)) { - // Replan SOA events. - replan_event(zone, old_zone, ZONE_EVENT_REFRESH); - replan_event(zone, old_zone, ZONE_EVENT_EXPIRE); - } else { - // Plan SOA events anew. - if (!zone_contents_is_empty(zone->contents)) { - const knot_rdataset_t *soa = node_rdataset(zone->contents->apex, - KNOT_RRTYPE_SOA); - assert(soa); - zone_events_schedule(zone, ZONE_EVENT_REFRESH, knot_soa_refresh(soa)); - } - } -} - -/*!< \brief Replans transfer event. */ -static void replan_xfer(zone_t *zone, const zone_t *old_zone) -{ - if (!zone_master(zone)) { - // Only valid for slaves. - return; - } - - if (zone_master(old_zone)) { - // Replan the transfer from old zone. - replan_event(zone, old_zone, ZONE_EVENT_XFER); - } else if (zone_contents_is_empty(zone->contents)) { - // Plan transfer anew. - zone->bootstrap_retry = bootstrap_next(zone->bootstrap_retry); - zone_events_schedule(zone, ZONE_EVENT_XFER, zone->bootstrap_retry); - } -} - -/*!< \brief Replans flush event. */ -static void replan_flush(zone_t *zone, const zone_t *old_zone) -{ - if (zone->conf->dbsync_timeout <= 0) { - // Immediate sync scheduled after events. - return; - } - - const time_t flush_time = zone_events_get_time(old_zone, ZONE_EVENT_FLUSH); - if (flush_time <= ZONE_EVENT_NOW) { - // Not scheduled previously. - zone_events_schedule(zone, ZONE_EVENT_FLUSH, zone->conf->dbsync_timeout); - return; - } - - // Pick time to schedule: either reuse or schedule sooner than old event. - const time_t schedule_at = MIN(time(NULL) + zone->conf->dbsync_timeout, flush_time); - zone_events_schedule_at(zone, ZONE_EVENT_FLUSH, schedule_at); -} - -/*!< \brief Creates new DDNS q in the new zone - q contains references from the old zone. */ -static void duplicate_ddns_q(zone_t *zone, zone_t *old_zone) -{ - struct request_data *d, *nxt; - WALK_LIST_DELSAFE(d, nxt, old_zone->ddns_queue) { - add_tail(&zone->ddns_queue, (node_t *)d); - } - zone->ddns_queue_size = old_zone->ddns_queue_size; - - // Reset the list, new zone will free the data. - init_list(&old_zone->ddns_queue); -} - -/*!< Replans DDNS event. */ -static void replan_update(zone_t *zone, zone_t *old_zone) -{ - pthread_mutex_lock(&old_zone->ddns_lock); - - const bool have_updates = old_zone->ddns_queue_size > 0; - if (have_updates) { - duplicate_ddns_q(zone, (zone_t *)old_zone); - } - - pthread_mutex_unlock(&old_zone->ddns_lock); - - if (have_updates) { - zone_events_schedule(zone, ZONE_EVENT_UPDATE, ZONE_EVENT_NOW); - } -} - -/*!< Replans DNSSEC event. Not whole resign needed, \todo #247 */ -static void replan_dnssec(zone_t *zone) -{ - if (zone->conf->dnssec_enable) { - /* Keys could have changed, force resign. */ - zone_events_schedule(zone, ZONE_EVENT_DNSSEC, ZONE_EVENT_NOW); - } -} - -/* -- internal API --------------------------------------------------------- */ - -static bool valid_event(zone_event_type_t type) -{ - return (type > ZONE_EVENT_INVALID && type < ZONE_EVENT_COUNT); -} - -/*! \brief Return remaining time to planned event (seconds). */ -static time_t time_until(time_t planned) -{ - time_t now = time(NULL); - return now < planned ? (planned - now) : 0; -} - -/*! - * \brief Find next scheduled zone event. - * - * \param events Zone events. - * - * \return Zone event type, or ZONE_EVENT_INVALID if no event is scheduled. - */ -static zone_event_type_t get_next_event(zone_events_t *events) -{ - if (!events) { - return ZONE_EVENT_INVALID; - } - - zone_event_type_t next_type = ZONE_EVENT_INVALID; - time_t next = 0; - - for (int i = 0; i < ZONE_EVENT_COUNT; i++) { - time_t current = events->time[i]; - if (current == 0) { - continue; - } - - if (next == 0 || current < next) { - next = current; - next_type = i; - } - } - - return next_type; -} - -/*! - * \brief Set time of a given event type. - */ -static void event_set_time(zone_events_t *events, zone_event_type_t type, time_t time) -{ - assert(events); - assert(valid_event(type)); - - events->time[type] = time; -} - -/*! - * \brief Get time of a given event type. - */ -static time_t event_get_time(zone_events_t *events, zone_event_type_t type) -{ - assert(events); - assert(valid_event(type)); - - return events->time[type]; -} - -/*! - * \brief Cancel scheduled item, schedule first enqueued item. - * - * The events mutex must be locked when calling this function. - */ -static void reschedule(zone_events_t *events) -{ - assert(events); - assert(pthread_mutex_trylock(&events->mx) == EBUSY); - - if (!events->event || events->running || events->frozen) { - return; - } - - zone_event_type_t type = get_next_event(events); - if (!valid_event(type)) { - return; - } - - time_t diff = time_until(event_get_time(events, type)); - - evsched_schedule(events->event, diff * 1000); -} - -/* -- callbacks control ---------------------------------------------------- */ - -typedef struct event_info_t { - zone_event_type_t type; - const zone_event_cb callback; - const char *name; -} event_info_t; - -static const event_info_t EVENT_INFO[] = { - { ZONE_EVENT_RELOAD, event_reload, "reload" }, - { ZONE_EVENT_REFRESH, event_refresh, "refresh" }, - { ZONE_EVENT_XFER, event_xfer, "transfer" }, - { ZONE_EVENT_UPDATE, event_update, "update" }, - { ZONE_EVENT_EXPIRE, event_expire, "expiration" }, - { ZONE_EVENT_FLUSH, event_flush, "journal flush" }, - { ZONE_EVENT_NOTIFY, event_notify, "notify" }, - { ZONE_EVENT_DNSSEC, event_dnssec, "DNSSEC resign" }, - { 0 } -}; - -static const event_info_t *get_event_info(zone_event_type_t type) -{ - const event_info_t *info; - for (info = EVENT_INFO; info->callback != NULL; info++) { - if (info->type == type) { - return info; - } - } - - assert(0); - return NULL; -} - -/*! - * \brief Zone event wrapper, expected to be called from a worker thread. - * - * 1. Takes the next planned event. - * 2. Resets the event's scheduled time. - * 3. Perform the event's callback. - * 4. Schedule next event planned event. - */ -static void event_wrap(task_t *task) -{ - assert(task); - assert(task->ctx); - - zone_t *zone = task->ctx; - zone_events_t *events = &zone->events; - - pthread_mutex_lock(&events->mx); - zone_event_type_t type = get_next_event(events); - if (!valid_event(type)) { - events->running = false; - pthread_mutex_unlock(&events->mx); - return; - } - event_set_time(events, type, 0); - pthread_mutex_unlock(&events->mx); - - const event_info_t *info = get_event_info(type); - int result = info->callback(zone); - if (result != KNOT_EOK) { - log_zone_error(zone->name, "zone %s failed (%s)", info->name, - knot_strerror(result)); - } - - pthread_mutex_lock(&events->mx); - events->running = false; - reschedule(events); - pthread_mutex_unlock(&events->mx); -} - -/*! - * \brief Called by scheduler thread if the event occurs. - */ -static int event_dispatch(event_t *event) -{ - assert(event); - assert(event->data); - - zone_events_t *events = event->data; - - pthread_mutex_lock(&events->mx); - if (!events->running && !events->frozen) { - events->running = true; - worker_pool_assign(events->pool, &events->task); - } - pthread_mutex_unlock(&events->mx); - - return KNOT_EOK; -} - -/* -- public API ----------------------------------------------------------- */ - -int zone_events_init(zone_t *zone) -{ - if (!zone) { - return KNOT_EINVAL; - } - - zone_events_t *events = &zone->events; - - memset(&zone->events, 0, sizeof(zone->events)); - pthread_mutex_init(&events->mx, NULL); - events->task.ctx = zone; - events->task.run = event_wrap; - - return KNOT_EOK; -} - -int zone_events_setup(zone_t *zone, worker_pool_t *workers, evsched_t *scheduler) -{ - if (!zone || !workers || !scheduler) { - return KNOT_EINVAL; - } - - event_t *event; - event = evsched_event_create(scheduler, event_dispatch, &zone->events); - if (!event) { - return KNOT_ENOMEM; - } - - zone->events.event = event; - zone->events.pool = workers; - - return KNOT_EOK; -} - -void zone_events_deinit(zone_t *zone) -{ - if (!zone) { - return; - } - - evsched_cancel(zone->events.event); - evsched_event_free(zone->events.event); - - pthread_mutex_destroy(&zone->events.mx); - - memset(&zone->events, 0, sizeof(zone->events)); -} - -void zone_events_schedule_at(zone_t *zone, zone_event_type_t type, time_t time) -{ - if (!zone || !valid_event(type)) { - return; - } - - zone_events_t *events = &zone->events; - - pthread_mutex_lock(&events->mx); - event_set_time(events, type, time); - reschedule(events); - pthread_mutex_unlock(&events->mx); -} - -void zone_events_enqueue(zone_t *zone, zone_event_type_t type) -{ - if (!zone || !valid_event(type)) { - return; - } - - zone_events_t *events = &zone->events; - - pthread_mutex_lock(&events->mx); - - /* Bypass scheduler if no event is running. */ - if (!events->running && !events->frozen) { - events->running = true; - event_set_time(events, type, ZONE_EVENT_IMMEDIATE); - worker_pool_assign(events->pool, &events->task); - pthread_mutex_unlock(&events->mx); - return; - } - - pthread_mutex_unlock(&events->mx); - - /* Execute as soon as possible. */ - zone_events_schedule(zone, type, ZONE_EVENT_NOW); -} - -void zone_events_schedule(zone_t *zone, zone_event_type_t type, unsigned dt) -{ - time_t abstime = time(NULL) + dt; - return zone_events_schedule_at(zone, type, abstime); -} - -void zone_events_cancel(zone_t *zone, zone_event_type_t type) -{ - zone_events_schedule_at(zone, type, 0); -} - -void zone_events_freeze(zone_t *zone) -{ - if (!zone) { - return; - } - - zone_events_t *events = &zone->events; - - /* Prevent new events being enqueued. */ - pthread_mutex_lock(&events->mx); - events->frozen = true; - pthread_mutex_unlock(&events->mx); - - /* Cancel current event. */ - evsched_cancel(events->event); -} - -void zone_events_start(zone_t *zone) -{ - if (!zone) { - return; - } - - pthread_mutex_lock(&zone->events.mx); - reschedule(&zone->events); - pthread_mutex_unlock(&zone->events.mx); -} - -time_t zone_events_get_time(const struct zone_t *zone, zone_event_type_t type) -{ - if (zone == NULL) { - return KNOT_EINVAL; - } - - time_t event_time = KNOT_ENOENT; - zone_events_t *events = (zone_events_t *)&zone->events; - - pthread_mutex_lock(&events->mx); - - /* Get next valid event. */ - if (valid_event(type)) { - event_time = event_get_time(events, type); - } - - pthread_mutex_unlock(&events->mx); - - return event_time; -} - -const char *zone_events_get_name(zone_event_type_t type) -{ - /* Get information about the event and time. */ - const event_info_t *info = get_event_info(type); - if (info == NULL) { - return NULL; - } - - return info->name; -} - -time_t zone_events_get_next(const struct zone_t *zone, zone_event_type_t *type) +/*! \brief Progressive bootstrap retry timer. */ +uint32_t bootstrap_next(uint32_t timer) { - if (zone == NULL || type == NULL) { - return KNOT_EINVAL; - } - - time_t next_time = KNOT_ENOENT; - zone_events_t *events = (zone_events_t *)&zone->events; - - pthread_mutex_lock(&events->mx); - - /* Get time of next valid event. */ - *type = get_next_event(events); - if (valid_event(*type)) { - next_time = event_get_time(events, *type); - } else { - *type = ZONE_EVENT_INVALID; + timer *= 2; + timer += knot_random_uint32_t() % BOOTSTRAP_RETRY; + if (timer > BOOTSTRAP_MAXTIME) { + timer = BOOTSTRAP_MAXTIME; } - - pthread_mutex_unlock(&events->mx); - - return next_time; -} - -void zone_events_update(zone_t *zone, const zone_t *old_zone) -{ - replan_soa_events(zone, old_zone); - replan_xfer(zone, old_zone); - replan_flush(zone, old_zone); - replan_event(zone, old_zone, ZONE_EVENT_NOTIFY); - replan_update(zone, (zone_t *)old_zone); - replan_dnssec(zone); + return timer; } -void zone_events_replan_ddns(struct zone_t *zone, const struct zone_t *old_zone) -{ - if (old_zone) { - replan_update(zone, (zone_t *)old_zone); - } -} diff --git a/src/knot/zone/events/handlers.h b/src/knot/zone/events/handlers.h new file mode 100644 index 0000000000000000000000000000000000000000..7d9f5f5e6361ba97b4d746cbd735162a0367fbef --- /dev/null +++ b/src/knot/zone/events/handlers.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "knot/zone/events/events.h" + +/*! \brief Reloads the potentially changed zone. */ +int event_reload(zone_t *zone); +/*! \brief Sends a SOA query to master. */ +int event_refresh(zone_t *zone); +/*! \brief Initiates transfer with master. */ +int event_xfer(zone_t *zone); +/*! \brief Processes DDNS updates in the zone's DDNS queue. */ +int event_update(zone_t *zone); +/*! \brief Empties in-memory zone contents. */ +int event_expire(zone_t *zone); +/*! \brief Flushes zone contents into text file. */ +int event_flush(zone_t *zone); +/*! \brief Sends notify to slaves. */ +int event_notify(zone_t *zone); +/*! \brief (re)Signs the zone using its DNSSEC keys. */ +int event_dnssec(zone_t *zone); + +/*! \brief Progressive bootstrap retry timer. */ +uint32_t bootstrap_next(uint32_t timer); + diff --git a/src/knot/zone/events/replan.c b/src/knot/zone/events/replan.c new file mode 100644 index 0000000000000000000000000000000000000000..1aecf49d907bb6ff7f95a2fa618ad23595f4cbd2 --- /dev/null +++ b/src/knot/zone/events/replan.c @@ -0,0 +1,137 @@ +/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "libknot/rrtype/soa.h" +#include "knot/zone/events/replan.h" +#include "knot/zone/events/handlers.h" +#include "knot/zone/zone.h" + +/* -- Zone event replanning functions --------------------------------------- */ + +/*!< \brief Replans event for new zone according to old zone. */ +static void replan_event(zone_t *zone, const zone_t *old_zone, zone_event_type_t e) +{ + const time_t event_time = zone_events_get_time(old_zone, e); + if (event_time > ZONE_EVENT_NOW) { + zone_events_schedule_at(zone, e, event_time); + } +} + +/*!< \brief Replans events that are dependent on the SOA record. */ +static void replan_soa_events(zone_t *zone, const zone_t *old_zone) +{ + if (!zone_master(zone)) { + // Events only valid for slaves. + return; + } + + if (zone_master(old_zone)) { + // Replan SOA events. + replan_event(zone, old_zone, ZONE_EVENT_REFRESH); + replan_event(zone, old_zone, ZONE_EVENT_EXPIRE); + } else { + // Plan SOA events anew. + if (!zone_contents_is_empty(zone->contents)) { + const knot_rdataset_t *soa = node_rdataset(zone->contents->apex, + KNOT_RRTYPE_SOA); + assert(soa); + zone_events_schedule(zone, ZONE_EVENT_REFRESH, knot_soa_refresh(soa)); + } + } +} + +/*!< \brief Replans transfer event. */ +static void replan_xfer(zone_t *zone, const zone_t *old_zone) +{ + if (!zone_master(zone)) { + // Only valid for slaves. + return; + } + + if (zone_master(old_zone)) { + // Replan the transfer from old zone. + replan_event(zone, old_zone, ZONE_EVENT_XFER); + } else if (zone_contents_is_empty(zone->contents)) { + // Plan transfer anew. + zone->bootstrap_retry = bootstrap_next(zone->bootstrap_retry); + zone_events_schedule(zone, ZONE_EVENT_XFER, zone->bootstrap_retry); + } +} + +/*!< \brief Replans flush event. */ +static void replan_flush(zone_t *zone, const zone_t *old_zone) +{ + if (zone->conf->dbsync_timeout <= 0) { + // Immediate sync scheduled after events. + return; + } + + const time_t flush_time = zone_events_get_time(old_zone, ZONE_EVENT_FLUSH); + if (flush_time <= ZONE_EVENT_NOW) { + // Not scheduled previously. + zone_events_schedule(zone, ZONE_EVENT_FLUSH, zone->conf->dbsync_timeout); + return; + } + + // Pick time to schedule: either reuse or schedule sooner than old event. + const time_t schedule_at = MIN(time(NULL) + zone->conf->dbsync_timeout, flush_time); + zone_events_schedule_at(zone, ZONE_EVENT_FLUSH, schedule_at); +} + +/*!< \brief Creates new DDNS q in the new zone - q contains references from the old zone. */ +static void duplicate_ddns_q(zone_t *zone, zone_t *old_zone) +{ + struct request_data *d, *nxt; + WALK_LIST_DELSAFE(d, nxt, old_zone->ddns_queue) { + add_tail(&zone->ddns_queue, (node_t *)d); + } + zone->ddns_queue_size = old_zone->ddns_queue_size; + + // Reset the list, new zone will free the data. + init_list(&old_zone->ddns_queue); +} + +/*!< Replans DNSSEC event. Not whole resign needed, \todo #247 */ +static void replan_dnssec(zone_t *zone) +{ + if (zone->conf->dnssec_enable) { + /* Keys could have changed, force resign. */ + zone_events_schedule(zone, ZONE_EVENT_DNSSEC, ZONE_EVENT_NOW); + } +} + +/*!< Replans DDNS event. */ +void replan_update(zone_t *zone, const zone_t *old_zone) +{ + const bool have_updates = old_zone->ddns_queue_size > 0; + if (have_updates) { + duplicate_ddns_q(zone, (zone_t *)old_zone); + } + + if (have_updates) { + zone_events_schedule(zone, ZONE_EVENT_UPDATE, ZONE_EVENT_NOW); + } +} + +void replan_events(zone_t *zone, const zone_t *old_zone) +{ + replan_soa_events(zone, old_zone); + replan_xfer(zone, old_zone); + replan_flush(zone, old_zone); + replan_event(zone, old_zone, ZONE_EVENT_NOTIFY); + replan_update(zone, (zone_t *)old_zone); + replan_dnssec(zone); +} diff --git a/src/knot/zone/events/replan.h b/src/knot/zone/events/replan.h new file mode 100644 index 0000000000000000000000000000000000000000..b7174cedbd428cd937cb55529279ef9975c23097 --- /dev/null +++ b/src/knot/zone/events/replan.h @@ -0,0 +1,26 @@ +/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "knot/zone/events/events.h" +#include "knot/zone/zone.h" + +/*! \brief Replans zone's events using old zone. */ +void replan_events(zone_t *zone, const zone_t *old_zone); + +/*! \brief Replans zone's DDNS events using old zone's DDNS queue. */ +void replan_update(zone_t *zone, const zone_t *old_zone); diff --git a/src/knot/zone/semantic-check.c b/src/knot/zone/semantic-check.c index be0821ae1f3537ae6ba25f257305837d6cc59396..cc06004a502893fba42d7ee6615ac62c80f6802b 100644 --- a/src/knot/zone/semantic-check.c +++ b/src/knot/zone/semantic-check.c @@ -181,7 +181,7 @@ static void log_error_from_node(err_handler_t *handler, handler->error_count++; - char *name = knot_dname_to_str(node->owner); + char *name = knot_dname_to_str_alloc(node->owner); const char *errmsg = error_messages[-error]; log_zone_warning(zone_name, "semantic check, node '%s' (%s%s%s)", diff --git a/src/knot/zone/timers.c b/src/knot/zone/timers.c new file mode 100644 index 0000000000000000000000000000000000000000..9492eb12065f0748a9f39eb863ef45bd74144e42 --- /dev/null +++ b/src/knot/zone/timers.c @@ -0,0 +1,230 @@ +/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "libknot/dname.h" +#include "common/namedb/namedb.h" +#include "common/namedb/namedb_lmdb.h" +#include "knot/zone/timers.h" +#include "knot/zone/zonedb.h" + +/* ---- Knot-internal event code to db key lookup tables ------------------ - */ + +#define PERSISTENT_EVENT_COUNT 3 + +enum { + KEY_REFRESH = 1, + KEY_EXPIRE, + KEY_FLUSH +}; + +// Do not change these mappings if you want backwards compatibility. +static const uint8_t event_id_to_key[ZONE_EVENT_COUNT] = { + [ZONE_EVENT_REFRESH] = KEY_REFRESH, + [ZONE_EVENT_EXPIRE] = KEY_EXPIRE, + [ZONE_EVENT_FLUSH] = KEY_FLUSH +}; + +static const int key_to_event_id[PERSISTENT_EVENT_COUNT + 1] = { + [KEY_REFRESH] = ZONE_EVENT_REFRESH, + [KEY_EXPIRE] = ZONE_EVENT_EXPIRE, + [KEY_FLUSH] = ZONE_EVENT_FLUSH +}; + +static bool known_event_key(uint8_t key) +{ + return key <= KEY_FLUSH; +} + +#define EVENT_KEY_PAIR_SIZE (sizeof(uint8_t) + sizeof(int64_t)) + +static bool event_persistent(size_t event) +{ + return event_id_to_key[event] != 0; +} + +/*! \brief Stores timers for persistent events. */ +static int store_timers(knot_txn_t *txn, zone_t *zone) +{ + // Create key + knot_val_t key = { .len = knot_dname_size(zone->name), .data = zone->name }; + + // Create value + uint8_t packed_timer[EVENT_KEY_PAIR_SIZE * PERSISTENT_EVENT_COUNT]; + size_t offset = 0; + for (zone_event_type_t event = 0; event < ZONE_EVENT_COUNT; ++event) { + if (!event_persistent(event)) { + continue; + } + + // Write event key and timer to buffer + packed_timer[offset] = event_id_to_key[event]; + offset += 1; + knot_wire_write_u64(packed_timer + offset, + (int64_t)zone_events_get_time(zone, event)); + offset += sizeof(uint64_t); + } + knot_val_t val = { .len = sizeof(packed_timer), .data = packed_timer }; + + // Store + return namedb_lmdb_api()->insert(txn, &key, &val, 0); +} + +/*! \brief Reads timers for persistent events. */ +static int read_timers(knot_txn_t *txn, const zone_t *zone, time_t *timers) +{ + const struct namedb_api *db_api = namedb_lmdb_api(); + + knot_val_t key = { .len = knot_dname_size(zone->name), .data = zone->name }; + knot_val_t val; + int ret = db_api->find(txn, &key, &val, 0); + if (ret != KNOT_EOK) { + if (ret == KNOT_ENOENT) { + // New zone, no entry in db. + memset(timers, 0, ZONE_EVENT_COUNT * sizeof(time_t)); + return KNOT_EOK; + } + return ret; + } + + // Set unknown/unset event timers to 0. + memset(timers, 0, ZONE_EVENT_COUNT * sizeof(time_t)); + + const size_t stored_event_count = val.len / EVENT_KEY_PAIR_SIZE; + size_t offset = 0; + for (size_t i = 0; i < stored_event_count; ++i) { + const uint8_t db_key = ((uint8_t *)val.data)[offset]; + offset += 1; + if (known_event_key(db_key)) { + const zone_event_type_t event = key_to_event_id[db_key]; + timers[event] = (time_t)knot_wire_read_u64(val.data + offset); + } + offset += sizeof(uint64_t); + } + + return KNOT_EOK; +} + +/* -------- API ------------------------------------------------------------- */ + +knot_namedb_t *open_timers_db(const char *storage) +{ +#ifndef HAVE_LMDB + // No-op if we don't have lmdb, all other operations will no-op as well then + return NULL; +#else + return namedb_lmdb_api()->init(storage, NULL); +#endif +} + +void close_timers_db(knot_namedb_t *timer_db) +{ + if (timer_db) { + namedb_lmdb_api()->deinit(timer_db); + } +} + +int read_zone_timers(knot_namedb_t *timer_db, const zone_t *zone, time_t *timers) +{ + if (timer_db == NULL) { + memset(timers, 0, ZONE_EVENT_COUNT * sizeof(time_t)); + return KNOT_EOK; + } + + const struct namedb_api *db_api = namedb_lmdb_api(); + + knot_txn_t txn; + int ret = db_api->txn_begin(timer_db, &txn, KNOT_NAMEDB_RDONLY); + if (ret != KNOT_EOK) { + return ret; + } + + ret = read_timers(&txn, zone, timers); + db_api->txn_abort(&txn); + if (ret != KNOT_EOK) { + return ret; + } + + return KNOT_EOK; +} + +int write_zone_timers(knot_namedb_t *timer_db, zone_t *zone) +{ + if (timer_db == NULL) { + return KNOT_EOK; + } + + const struct namedb_api *db_api = namedb_lmdb_api(); + + knot_txn_t txn; + int ret = db_api->txn_begin(timer_db, &txn, 0); + if (ret != KNOT_EOK) { + return ret; + } + + ret = store_timers(&txn, zone); + if (ret != KNOT_EOK) { + db_api->txn_abort(&txn); + return ret; + } + + return db_api->txn_commit(&txn); +} + +int sweep_timer_db(knot_namedb_t *timer_db, knot_zonedb_t *zone_db) +{ + if (timer_db == NULL) { + return KNOT_EOK; + } + const struct namedb_api *db_api = namedb_lmdb_api(); + + knot_txn_t txn; + int ret = db_api->txn_begin(timer_db, &txn, KNOT_NAMEDB_SORTED); + if (ret != KNOT_EOK) { + return ret; + } + + if (db_api->count(&txn) == 0) { + db_api->txn_abort(&txn); + return KNOT_EOK; + } + + knot_iter_t *it = db_api->iter_begin(&txn, 0); + if (it == NULL) { + db_api->txn_abort(&txn); + return KNOT_ERROR; + } + + while (it) { + knot_val_t key; + ret = db_api->iter_key(it, &key); + if (ret != KNOT_EOK) { + db_api->txn_abort(&txn); + return ret; + } + const knot_dname_t *dbkey = (const knot_dname_t *)key.data; + if (!knot_zonedb_find(zone_db, dbkey)) { + // Delete obsolete timers + db_api->del(&txn, &key); + } + + it = db_api->iter_next(it); + } + db_api->iter_finish(it); + + return db_api->txn_commit(&txn); +} + + diff --git a/src/knot/zone/timers.h b/src/knot/zone/timers.h new file mode 100644 index 0000000000000000000000000000000000000000..caa2772ac7082302a14091036ecdf73d17598ffa --- /dev/null +++ b/src/knot/zone/timers.h @@ -0,0 +1,72 @@ +/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "common/namedb/namedb.h" +#include "knot/zone/zone.h" +#include "knot/zone/zonedb.h" + +/*! + * \brief Opens zone timers db. No-op without LMDB support. + * + * \param storage Path to storage directory. + * + * \return Created database. + */ +knot_namedb_t *open_timers_db(const char *storage); + +/*! + * \brief Closes zone timers db. + * + * \param timer_db Timer database. + */ +void close_timers_db(knot_namedb_t *timer_db); + +/*! + * \brief Reads zone timers from timers db. + * Currently these events are read (and stored): + * ZONE_EVENT_REFRESH + * ZONE_EVENT_EXPIRE + * ZONE_EVENT_FLUSH + * + * \param timer_db Timer database. + * \param zone Zone to read timers for. + * \param timers Output array with timers (size must be ZONE_EVENT_COUNT). + * + * \return KNOT_E* + */ +int read_zone_timers(knot_namedb_t *timer_db, const zone_t *zone, time_t *timers); + +/*! + * \brief Writes zone timers to timers db. + * + * \param timer_db Timer database. + * \param zone Zone to store timers for. + * + * \return KNOT_E* + */ +int write_zone_timers(knot_namedb_t *timer_db, zone_t *zone); + +/*! + * \brief Removes stale zones info from timers db. + * + * \param timer_db Timer database. + * \param zone_db Current zone database. + * \return KNOT_EOK or an error + */ +int sweep_timer_db(knot_namedb_t *timer_db, knot_zonedb_t *zone_db); + diff --git a/src/knot/zone/zone-tree.c b/src/knot/zone/zone-tree.c index ef601ec07996eaa232737f339bc6d9ce90efef71..370a45d1e89395e7ae827182a4967ee6b070ac91 100644 --- a/src/knot/zone/zone-tree.c +++ b/src/knot/zone/zone-tree.c @@ -170,9 +170,9 @@ int zone_tree_get_less_or_equal(zone_tree_t *tree, } dbg_zone_exec_detail( - char *name = knot_dname_to_str(owner); + char *name = knot_dname_to_str_alloc(owner); char *name_f = (*found != NULL) - ? knot_dname_to_str((*found)->owner) + ? knot_dname_to_str_alloc((*found)->owner) : "none"; dbg_zone_detail("Searched for owner %s in zone tree.\n", @@ -214,7 +214,7 @@ zone_node_t *zone_tree_get_next(zone_tree_t *tree, fval = hattrie_iter_val(it); hattrie_iter_free(it); } - + n = (zone_node_t *)*fval; /* Next node must be non-empty and auth. */ if (n->rrset_count == 0 || n->flags & NODE_FLAGS_NONAUTH) { diff --git a/src/knot/zone/zone.c b/src/knot/zone/zone.c index 56000527db5e00e25ff0805a20ff4ae322c665b9..e3557ebbba4eef651c8da7f2c4544d8c59e05b6e 100644 --- a/src/knot/zone/zone.c +++ b/src/knot/zone/zone.c @@ -60,7 +60,7 @@ zone_t* zone_new(conf_zone_t *conf) } memset(zone, 0, sizeof(zone_t)); - zone->name = knot_dname_from_str(conf->name); + zone->name = knot_dname_from_str_alloc(conf->name); knot_dname_to_lower(zone->name); if (zone->name == NULL) { free(zone); diff --git a/src/knot/zone/zone.h b/src/knot/zone/zone.h index 50d0a9ccdfbb40d0f049d1a24e0bf235bb815ce3..069f60c8f92f7eb753d5dee3c2128e89f1d0bd8f 100644 --- a/src/knot/zone/zone.h +++ b/src/knot/zone/zone.h @@ -33,7 +33,7 @@ #include "knot/conf/conf.h" #include "knot/server/journal.h" #include "knot/updates/acl.h" -#include "knot/zone/events.h" +#include "knot/zone/events/events.h" #include "knot/zone/contents.h" #include "libknot/dname.h" diff --git a/src/knot/zone/zonedb-load.c b/src/knot/zone/zonedb-load.c index 6bb1f6da7eed8a15cc38505d388c9eace6b604cc..97f938edf0d81c074b8c15d80ef10ef07acf3164 100644 --- a/src/knot/zone/zonedb-load.c +++ b/src/knot/zone/zonedb-load.c @@ -24,6 +24,7 @@ #include "knot/zone/zone.h" #include "knot/zone/zonefile.h" #include "knot/zone/zonedb.h" +#include "knot/zone/timers.h" #include "knot/server/server.h" #include "libknot/dname.h" @@ -112,66 +113,153 @@ static void log_zone_load_info(const zone_t *zone, const char *zone_name, log_zone_info(zone->name, "zone %s, serial %u", action, serial); } -/*! - * \brief Load or reload the zone. - * - * \param conf Zone configuration. - * \param server Server. - * \param old_zone Already loaded zone (can be NULL). - * - * \return Error code, KNOT_EOK if successful. - */ -static zone_t *create_zone(conf_zone_t *conf, server_t *server, zone_t *old_zone) +static zone_t *create_zone_from(conf_zone_t *zone_conf, server_t *server) { - assert(conf); - assert(server); - - zone_t *zone = zone_new(conf); + zone_t *zone = zone_new(zone_conf); if (!zone) { return NULL; } - if (old_zone) { - zone->contents = old_zone->contents; - } - - zone_status_t zstatus = zone_file_status(old_zone, conf); - int result = zone_events_setup(zone, server->workers, &server->sched); if (result != KNOT_EOK) { zone->conf = NULL; zone_free(&zone); return NULL; } + + return zone; +} +static zone_t *create_zone_reload(const conf_t *conf, conf_zone_t *zone_conf, server_t *server, + zone_t *old_zone) +{ + zone_t *zone = create_zone_from(zone_conf, server); + if (!zone) { + return NULL; + } + zone->contents = old_zone->contents; + + const zone_status_t zstatus = zone_file_status(old_zone, zone_conf); + switch (zstatus) { - case ZONE_STATUS_FOUND_NEW: case ZONE_STATUS_FOUND_UPDATED: /* Enqueueing makes the first zone load waitable. */ zone_events_enqueue(zone, ZONE_EVENT_RELOAD); /* Replan DDNS processing if there are pending updates. */ zone_events_replan_ddns(zone, old_zone); break; - case ZONE_STATUS_BOOSTRAP: - zone_events_schedule(zone, ZONE_EVENT_REFRESH, ZONE_EVENT_NOW); - break; - case ZONE_STATUS_NOT_FOUND: - break; case ZONE_STATUS_FOUND_CURRENT: - assert(old_zone); zone->zonefile_mtime = old_zone->zonefile_mtime; zone->zonefile_serial = old_zone->zonefile_serial; + /* Reuse events from old zone. */ zone_events_update(zone, old_zone); + /* Write updated timers. */ + write_zone_timers(conf->timers_db, zone); break; default: assert(0); } + + return zone; +} - log_zone_load_info(zone, conf->name, zstatus); +static bool slave_event(zone_event_type_t event) +{ + return event == ZONE_EVENT_EXPIRE || event == ZONE_EVENT_REFRESH; +} +static void reuse_uvents(zone_t *zone, const time_t *timers) +{ + for (zone_event_type_t event = 0; event < ZONE_EVENT_COUNT; ++event) { + if (timers[event] == 0) { + // Timer unset. + continue; + } + if (slave_event(event) && !zone_master(zone)) { + // Slave-only event. + continue; + } + + zone_events_schedule_at(zone, event, timers[event]); + } +} + +static bool zone_expired(const time_t *timers) +{ + const time_t now = time(NULL); + return now <= timers[ZONE_EVENT_EXPIRE]; +} + +static zone_t *create_zone_new(conf_zone_t *zone_conf, server_t *server) +{ + zone_t *zone = create_zone_from(zone_conf, server); + if (!zone) { + return NULL; + } + + time_t timers[ZONE_EVENT_COUNT]; + memset(timers, 0, sizeof(timers)); + + // Get persistent timers + int ret = read_zone_timers(conf()->timers_db, zone, timers); + if (ret != KNOT_EOK) { + log_zone_error(zone->name, "cannot read zone timers (%s)", + knot_strerror(ret)); + zone->conf = NULL; + zone_free(&zone); + return NULL; + } + + reuse_uvents(zone, timers); + + const zone_status_t zstatus = zone_file_status(NULL, zone_conf); + + switch (zstatus) { + case ZONE_STATUS_FOUND_NEW: + if (!zone_expired(timers)) { + /* Enqueueing makes the first zone load waitable. */ + zone_events_enqueue(zone, ZONE_EVENT_RELOAD); + } + break; + case ZONE_STATUS_BOOSTRAP: + if (timers[ZONE_EVENT_REFRESH] == 0) { + // Plan immediate refresh if not already planned. + zone_events_schedule(zone, ZONE_EVENT_REFRESH, ZONE_EVENT_NOW); + } + break; + case ZONE_STATUS_NOT_FOUND: + break; + default: + assert(0); + } + + log_zone_load_info(zone, zone_conf->name, zstatus); + return zone; } +/*! + * \brief Load or reload the zone. + * + * \param zone_conf Zone configuration. + * \param server Server. + * \param old_zone Already loaded zone (can be NULL). + * + * \return Error code, KNOT_EOK if successful. + */ +static zone_t *create_zone(const conf_t *conf, conf_zone_t *zone_conf, server_t *server, + zone_t *old_zone) +{ + assert(zone_conf); + assert(server); + + if (old_zone) { + return create_zone_reload(conf, zone_conf, server, old_zone); + } else { + return create_zone_new(zone_conf, server); + } +} + /*! * \brief Create new zone database. * @@ -199,11 +287,11 @@ static knot_zonedb_t *create_zonedb(const conf_t *conf, server_t *server) conf_zone_t *zone_config = (conf_zone_t *)*hattrie_iter_val(it); - knot_dname_t *apex = knot_dname_from_str(zone_config->name); + knot_dname_t *apex = knot_dname_from_str_alloc(zone_config->name); zone_t *old_zone = knot_zonedb_find(db_old, apex); knot_dname_free(&apex, NULL); - zone_t *zone = create_zone(zone_config, server, old_zone); + zone_t *zone = create_zone(conf, zone_config, server, old_zone); if (!zone) { log_zone_str_error(zone_config->name, "zone cannot be created"); @@ -282,6 +370,9 @@ int zonedb_reload(const conf_t *conf, struct server_t *server) /* Wait for readers to finish reading old zone database. */ synchronize_rcu(); + + /* Sweep the timer database. */ + sweep_timer_db(conf->timers_db, db_new); /* * Remove all zones present in the new DB from the old DB. diff --git a/src/knot/zone/zonefile.c b/src/knot/zone/zonefile.c index 70ee8e988f08e280c1c17c3a0c723ef887553bd6..2f1b24254c19023493ffb6fffd558ae61cdfc99b 100644 --- a/src/knot/zone/zonefile.c +++ b/src/knot/zone/zonefile.c @@ -63,7 +63,7 @@ static bool handle_err(zcreator_t *zc, const zone_node_t *node, const knot_rrset_t *rr, int ret, bool master) { const knot_dname_t *zname = zc->z->apex->owner; - char *rrname = rr ? knot_dname_to_str(rr->owner) : NULL; + char *rrname = rr ? knot_dname_to_str_alloc(rr->owner) : NULL; if (ret == KNOT_EOUTOFZONE) { WARNING(zname, "ignoring out-of-zone data, owner '%s'", rrname ? rrname : "unknown"); @@ -164,7 +164,7 @@ static void scanner_process(zs_scanner_t *scanner) knot_rrset_init(&rr, owner, scanner->r_type, scanner->r_class); int ret = add_rdata_to_rr(&rr, scanner); if (ret != KNOT_EOK) { - char *rr_name = knot_dname_to_str(rr.owner); + char *rr_name = knot_dname_to_str_alloc(rr.owner); const knot_dname_t *zname = zc->z->apex->owner; ERROR(zname, "failed to add RDATA, file '%s', line %"PRIu64", owner '%s'", scanner->file.name, scanner->line_counter, rr_name); @@ -184,7 +184,7 @@ static zone_contents_t *create_zone_from_name(const char *origin) if (origin == NULL) { return NULL; } - knot_dname_t *owner = knot_dname_from_str(origin); + knot_dname_t *owner = knot_dname_from_str_alloc(origin); if (owner == NULL) { return NULL; } diff --git a/src/libknot/consts.h b/src/libknot/consts.h index adaa18c256e5dc68e809770f3df73c5fe0b105cb..1bac3ac783493a98a874ede6d883336e2259fe96 100644 --- a/src/libknot/consts.h +++ b/src/libknot/consts.h @@ -34,8 +34,9 @@ /*! * \brief Basic limits for domain names (RFC 1035). */ -#define KNOT_DNAME_MAXLEN 255 /*!< 1-byte maximum. */ -#define KNOT_DNAME_MAXLABELS 127 /*!< 1-char labels. */ +#define KNOT_DNAME_MAXLEN 255 /*!< 1-byte maximum. */ +#define KNOT_DNAME_MAXLABELS 127 /*!< 1-char labels. */ +#define KNOT_DNAME_MAXLABELLEN 63 /*!< 2^6 - 1 */ /*! * \brief Address family numbers. diff --git a/src/libknot/dname.c b/src/libknot/dname.c index e5ad1b8494e5eb17b9880f55fcfe18ab9578b196..31b7a6b88313fbcd1d130547349711f1e79eef20 100644 --- a/src/libknot/dname.c +++ b/src/libknot/dname.c @@ -73,8 +73,8 @@ int knot_dname_wire_check(const uint8_t *name, const uint8_t *endp, is_compressed = true; } } else { - /* Check label length (maximum 63 bytes allowed). */ - if (*name > 63) + /* Check label length. */ + if (*name > KNOT_DNAME_MAXLABELLEN) return KNOT_EMALF; /* Check if there's enough space. */ int lblen = *name + 1; @@ -211,17 +211,24 @@ int knot_dname_unpack(uint8_t* dst, const knot_dname_t *src, /*----------------------------------------------------------------------------*/ -char *knot_dname_to_str(const knot_dname_t *name) +char *knot_dname_to_str(char *dst, const knot_dname_t *name, size_t maxlen) { - if (name == NULL) + if (name == NULL) { return NULL; + } - /*! \todo Supply packet. */ - /*! \todo Write to static buffer? */ - // Allocate space for dname string + 1 char termination. int dname_size = knot_dname_size(name); - size_t alloc_size = dname_size + 1; - char *res = malloc(alloc_size); + if (dname_size <= 0) { + return NULL; + } + + /* Check the size for len(dname) + 1 char termination. */ + size_t alloc_size = (dst == NULL) ? dname_size + 1 : maxlen; + if (alloc_size < dname_size + 1) { + return NULL; + } + + char *res = (dst == NULL) ? malloc(alloc_size) : dst; if (res == NULL) { return NULL; } @@ -232,11 +239,11 @@ char *knot_dname_to_str(const knot_dname_t *name) for (uint i = 0; i < dname_size; i++) { uint8_t c = name[i]; - // Read next label size. + /* Read next label size. */ if (label_len == 0) { label_len = c; - // Write label separation. + /* Write label separation. */ if (str_len > 0 || dname_size == 1) { res[str_len++] = '.'; } @@ -253,33 +260,47 @@ char *knot_dname_to_str(const knot_dname_t *name) * encoded in \ddd notation. */ - // Increase output size for \x format. - alloc_size += 1; - char *extended = realloc(res, alloc_size); - if (extended == NULL) { - free(res); - return NULL; + if (dst != NULL) { + if (maxlen <= str_len + 2) { + return NULL; + } + } else { + /* Extend output buffer for \x format. */ + alloc_size += 1; + char *extended = realloc(res, alloc_size); + if (extended == NULL) { + free(res); + return NULL; + } + res = extended; } - res = extended; - // Write encoded character. + /* Write encoded character. */ res[str_len++] = '\\'; res[str_len++] = c; } else { - // Increase output size for \DDD format. - alloc_size += 3; - char *extended = realloc(res, alloc_size); - if (extended == NULL) { - free(res); - return NULL; + if (dst != NULL) { + if (maxlen <= str_len + 4) { + return NULL; + } + } else { + /* Extend output buffer for \DDD format. */ + alloc_size += 3; + char *extended = realloc(res, alloc_size); + if (extended == NULL) { + free(res); + return NULL; + } + res = extended; } - res = extended; - // Write encoded character. + /* Write encoded character. */ int ret = snprintf(res + str_len, alloc_size - str_len, "\\%03u", c); if (ret <= 0 || ret >= alloc_size - str_len) { - free(res); + if (dst == NULL) { + free(res); + } return NULL; } @@ -289,7 +310,7 @@ char *knot_dname_to_str(const knot_dname_t *name) label_len--; } - // String_termination. + /* String_termination. */ res[str_len] = 0; return res; @@ -297,60 +318,143 @@ char *knot_dname_to_str(const knot_dname_t *name) /*----------------------------------------------------------------------------*/ -knot_dname_t *knot_dname_from_str(const char *name) +knot_dname_t *knot_dname_from_str(uint8_t *dst, const char *name, size_t maxlen) { if (name == NULL) { return NULL; } - unsigned len = strlen(name); - if (len > KNOT_DNAME_MAXLEN) { + size_t name_len = strlen(name); + if (name_len == 0) { return NULL; } - /* Estimate wire size for special cases. */ - unsigned wire_size = len + 1; - if (name[0] == '.' && len == 1) { - wire_size = 1; /* Root label. */ - len = 0; /* Do not parse input. */ - } else if (name[len - 1] != '.') { - ++wire_size; /* No FQDN, reserve last root label. */ + /* Wire size estimation. */ + size_t alloc_size = maxlen; + if (dst == NULL) { + /* Check for the root label. */ + if (name[0] == '.') { + /* Just the root dname can begin with a dot. */ + if (name_len > 1) { + return NULL; + } + name_len = 0; /* Skip the following parsing. */ + alloc_size = 1; + } else if (name[name_len - 1] != '.') { /* Check for non-FQDN. */ + alloc_size = 1 + name_len + 1; + } else { + alloc_size = 1 + name_len ; /* + 1 ~ first label length. */ + } } - /* Create wire. */ - uint8_t *wire = malloc(wire_size * sizeof(uint8_t)); - if (wire == NULL) + /* The minimal (root) dname takes 1 byte. */ + if (alloc_size == 0) { return NULL; - *wire = '\0'; + } + + /* Check the maximal wire size. */ + if (alloc_size > KNOT_DNAME_MAXLEN) { + alloc_size = KNOT_DNAME_MAXLEN; + } + + /* Prepare output buffer. */ + uint8_t *wire = (dst == NULL) ? malloc(alloc_size) : dst; + if (wire == NULL) { + return NULL; + } - /* Parse labels. */ - const uint8_t *ch = (const uint8_t *)name; - const uint8_t *np = ch + len; uint8_t *label = wire; - uint8_t *w = wire + 1; /* Reserve 1 for label len */ - while (ch != np) { - if (*ch == '.') { - /* Zero-length label inside a dname - invalid. */ - if (*label == 0) { - free(wire); - return NULL; + uint8_t *wire_pos = wire + 1; + uint8_t *wire_end = wire + alloc_size; + + /* Initialize the first label (root label). */ + *label = 0; + + const uint8_t *ch = (const uint8_t *)name; + const uint8_t *end = ch + name_len; + + while (ch < end) { + /* Check the output buffer for enough space. */ + if (wire_pos >= wire_end) { + goto dname_from_str_failed; + } + + switch (*ch) { + case '.': + /* Check for invalid zero-length label. */ + if (*label == 0 && name_len > 1) { + goto dname_from_str_failed; } - label = w; - *label = '\0'; - } else { - *w = *ch; - *label += 1; + label = wire_pos++; + *label = 0; + break; + case '\\': + ch++; + + /* At least one more character is required OR + * check for maximal label length. + */ + if (ch == end || ++(*label) > KNOT_DNAME_MAXLABELLEN) { + goto dname_from_str_failed; + } + + /* Check for \DDD notation. */ + if (isdigit(*ch) != 0) { + /* Check for next two digits. */ + if (ch + 2 >= end || + isdigit(*(ch + 1)) == 0 || + isdigit(*(ch + 2)) == 0) { + goto dname_from_str_failed; + } + + uint32_t num = (*(ch + 0) - '0') * 100 + + (*(ch + 1) - '0') * 10 + + (*(ch + 2) - '0') * 1; + if (num > UINT8_MAX) { + goto dname_from_str_failed; + } + *(wire_pos++) = num; + ch +=2; + } else { + *(wire_pos++) = *ch; + } + break; + default: + /* Check for maximal label length. */ + if (++(*label) > KNOT_DNAME_MAXLABELLEN) { + goto dname_from_str_failed; + } + *(wire_pos++) = *ch; } - ++w; - ++ch; + ch++; } /* Check for non-FQDN name. */ if (*label > 0) { - *w = '\0'; + if (wire_pos >= wire_end) { + goto dname_from_str_failed; + } + *(wire_pos++) = 0; + } + + /* Reduce output buffer if the size is overestimated. */ + if (wire_pos < wire_end && dst == NULL) { + uint8_t *reduced = realloc(wire, wire_pos - wire); + if (reduced == NULL) { + goto dname_from_str_failed; + } + wire = reduced; } return wire; + +dname_from_str_failed: + + if (dst == NULL) { + free(wire); + } + + return NULL; } /*----------------------------------------------------------------------------*/ diff --git a/src/libknot/dname.h b/src/libknot/dname.h index 58bfc174e03d347572c1a54cee3d480ed833a746..db7af67c6bd21a6d6191659f81a8aee5b793f543 100644 --- a/src/libknot/dname.h +++ b/src/libknot/dname.h @@ -118,31 +118,51 @@ int knot_dname_unpack(uint8_t *dst, const knot_dname_t *src, size_t maxlen, const uint8_t *pkt); /*! - * \brief Converts the given domain name to string representation. + * \brief Converts the given domain name to its string representation. * - * \note Allocates new memory, remember to free it. + * \note Output buffer is allocated automatically if dst is NULL. * - * \todo The function doesn't process escaped characters like \DDD or \X. + * \param dst Output buffer. + * \param name Domain name to be converted. + * \param maxlen Output buffer length. * - * \param name Domain name to be converted. - * - * \return 0-terminated string representing the given domain name in - * presentation format. + * \return 0-terminated string if successful, NULL if error. + */ +char *knot_dname_to_str(char *dst, const knot_dname_t *name, size_t maxlen); + +/*! + * \brief This function is a shortcut for \ref knot_dname_to_str with + * no output buffer parameters. */ -char *knot_dname_to_str(const knot_dname_t *name); +static inline char *knot_dname_to_str_alloc(const knot_dname_t *name) +{ + return knot_dname_to_str(NULL, name, 0); +} /*! * \brief Creates a dname structure from domain name given in presentation * format. * - * The resulting FQDN is stored in the wire format. + * \note The resulting FQDN is stored in the wire format. + * \note Output buffer is allocated automatically if dst is NULL. * - * \param name Domain name in presentation format (labels separated by dots, - * '\0' terminated). + * \param dst Output buffer. + * \param name Domain name in presentation format (labels separated by dots, + * '\0' terminated). + * \param maxlen Output buffer length. * - * \return New name or NULL + * \return New dname if successful, NULL if error. + */ +knot_dname_t *knot_dname_from_str(uint8_t *dst, const char *name, size_t maxlen); + +/*! + * \brief This function is a shortcut for \ref knot_dname_from_str with + * no output buffer parameters. */ -knot_dname_t *knot_dname_from_str(const char *name); +static inline knot_dname_t *knot_dname_from_str_alloc(const char *name) +{ + return knot_dname_from_str(NULL, name, 0); +} /*! * \brief Convert name to lowercase. diff --git a/src/libknot/dnssec/key.c b/src/libknot/dnssec/key.c index dc6fff2894e68b6ae1f16264ebcbc85f262c3b56..2b9cce3102257bec449d69ca3f725d334675bc50 100644 --- a/src/libknot/dnssec/key.c +++ b/src/libknot/dnssec/key.c @@ -528,7 +528,7 @@ int knot_tsig_create_key(const char *name, int algorithm, } knot_dname_t *dname; - dname = knot_dname_from_str(name); + dname = knot_dname_from_str_alloc(name); if (!dname) { return KNOT_ENOMEM; } diff --git a/src/libknot/errcode.c b/src/libknot/errcode.c index 68fd8336bb8bcf86ece5675dea8a541df3a9eecb..a85f6a9d05fdc9fca4f01943abeb14851b24ba16 100644 --- a/src/libknot/errcode.c +++ b/src/libknot/errcode.c @@ -39,6 +39,7 @@ const error_table_t error_messages[] = { { KNOT_EISCONN, "already connected" }, { KNOT_EADDRINUSE, "address already in use" }, { KNOT_ENOENT, "not exists" }, + { KNOT_EEXIST, "already exists" }, { KNOT_ERANGE, "value is out of range" }, /* General errors. */ diff --git a/src/libknot/errcode.h b/src/libknot/errcode.h index c91fa46d91edb30fc4c631d73694e22f05bf5248..88bec58fc42a2ead7846a24488117f5f516d1e11 100644 --- a/src/libknot/errcode.h +++ b/src/libknot/errcode.h @@ -55,6 +55,7 @@ enum knot_error { KNOT_EISCONN = knot_errno_to_error(EISCONN), KNOT_EADDRINUSE = knot_errno_to_error(EADDRINUSE), KNOT_ENOENT = knot_errno_to_error(ENOENT), + KNOT_EEXIST = knot_errno_to_error(EEXIST), KNOT_ERANGE = knot_errno_to_error(ERANGE), /* General errors. */ diff --git a/src/libknot/rrset-dump.c b/src/libknot/rrset-dump.c index 75f3590e2031d8aa0e6b6f12ca577fa29b4089b2..647ea6b95752cad3054b70abcdb23ce300e7ed0d 100644 --- a/src/libknot/rrset-dump.c +++ b/src/libknot/rrset-dump.c @@ -804,16 +804,23 @@ static void wire_dname_to_str(rrset_dump_params_t *p) } // Write dname string. - char *dname_str = knot_dname_to_str(p->in); - if (p->style->ascii_to_idn != NULL) { + if (p->style->ascii_to_idn == NULL) { + char *dname_str = knot_dname_to_str(p->out, p->in, p->out_max); + if (dname_str == NULL) { + return; + } + out_len = strlen(dname_str); + } else { + char *dname_str = knot_dname_to_str_alloc(p->in); p->style->ascii_to_idn(&dname_str); + + int ret = snprintf(p->out, p->out_max, "%s", dname_str); + free(dname_str); + if (ret < 0 || (size_t)ret >= p->out_max) { + return; + } + out_len = ret; } - int ret = snprintf(p->out, p->out_max, "%s", dname_str); - free(dname_str); - if (ret < 0 || (size_t)ret >= p->out_max) { - return; - } - out_len = ret; // Fill in output. p->in += in_len; @@ -1831,7 +1838,7 @@ int knot_rrset_txt_dump_header(const knot_rrset_t *rrset, int ret; // Dump rrset owner. - char *name = knot_dname_to_str(rrset->owner); + char *name = knot_dname_to_str_alloc(rrset->owner); if (style->ascii_to_idn != NULL) { style->ascii_to_idn(&name); } diff --git a/src/libknot/rrtype/tsig.c b/src/libknot/rrtype/tsig.c index 0f8ea5f5f9bb9377adbd542e0ff29a7f0cfec215..ee2b99871f9a5e260025d75b9018adc5be0c4616 100644 --- a/src/libknot/rrtype/tsig.c +++ b/src/libknot/rrtype/tsig.c @@ -241,7 +241,7 @@ knot_tsig_algorithm_t tsig_rdata_alg(const knot_rrset_t *tsig) } /* Convert alg name to string. */ - char *name = knot_dname_to_str(alg_name); + char *name = knot_dname_to_str_alloc(alg_name); if (!name) { dbg_tsig("TSIG: rdata: cannot convert alg name.\n"); return KNOT_TSIG_ALG_NULL; @@ -336,7 +336,7 @@ int tsig_alg_from_name(const knot_dname_t *alg_name) return 0; } - char *name = knot_dname_to_str(alg_name); + char *name = knot_dname_to_str_alloc(alg_name); if (!name) { return 0; } diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c index a35e0842f604fc1078feb96c0a5a83abdaaab72e..b3a8a81e29fe36bc308794a85362508ba4d0b704 100644 --- a/src/libknot/tsig-op.c +++ b/src/libknot/tsig-op.c @@ -69,7 +69,7 @@ static int knot_tsig_check_key(const knot_rrset_t *tsig_rr, return KNOT_EMALF; } - char *name = knot_dname_to_str(tsig_name); + char *name = knot_dname_to_str_alloc(tsig_name); if (!name) { return KNOT_EMALF; } diff --git a/src/utils/common/exec.c b/src/utils/common/exec.c index b146a79a8c55e2e43ced029cb2eb7ccd3df1c725..a5f5b2e86002f914b939ea9843b8c4831cb354ac 100644 --- a/src/utils/common/exec.c +++ b/src/utils/common/exec.c @@ -382,7 +382,7 @@ static void print_section_host(const knot_rrset_t *rrsets, char type[32] = "NULL"; char *owner; - owner = knot_dname_to_str(rrset->owner); + owner = knot_dname_to_str_alloc(rrset->owner); if (style->style.ascii_to_idn != NULL) { style->style.ascii_to_idn(&owner); } @@ -438,7 +438,7 @@ static void print_error_host(const uint16_t code, knot_lookup_table_t *rcode; - owner = knot_dname_to_str(knot_pkt_qname(packet)); + owner = knot_dname_to_str_alloc(knot_pkt_qname(packet)); if (style->style.ascii_to_idn != NULL) { style->style.ascii_to_idn(&owner); } @@ -493,7 +493,7 @@ void print_header_xfr(const knot_pkt_t *packet, const style_t *style) } if (style->show_header) { - char *owner = knot_dname_to_str(knot_pkt_qname(packet)); + char *owner = knot_dname_to_str_alloc(knot_pkt_qname(packet)); if (style->style.ascii_to_idn != NULL) { style->style.ascii_to_idn(&owner); } diff --git a/src/utils/common/params.c b/src/utils/common/params.c index 3e308834cfef4f796da0f4bf6840ea418d73d6b2..0058e3a3aacf3389981050a75fba4e5695d67193 100644 --- a/src/utils/common/params.c +++ b/src/utils/common/params.c @@ -434,7 +434,7 @@ int params_parse_tsig(const char *value, knot_key_params_t *key_params) } /* Set key name and secret. */ - key_params->name = knot_dname_from_str(k); + key_params->name = knot_dname_from_str_alloc(k); knot_dname_to_lower(key_params->name); int r = knot_binary_from_base64(s, &key_params->secret); if (r != KNOT_EOK) { diff --git a/src/utils/dig/dig_exec.c b/src/utils/dig/dig_exec.c index 2dc8d16844213fb38a9a32a1f83e1c7101715635..e5f5f3b32bc69b5f594d2244f9f6108036b64567 100644 --- a/src/utils/dig/dig_exec.c +++ b/src/utils/dig/dig_exec.c @@ -315,7 +315,7 @@ static knot_pkt_t* create_query_packet(const query_t *query) } // Create QNAME from string. - knot_dname_t *qname = knot_dname_from_str(query->owner); + knot_dname_t *qname = knot_dname_from_str_alloc(query->owner); if (qname == NULL) { knot_pkt_free(&packet); return NULL; diff --git a/src/utils/nsec3hash/nsec3hash_main.c b/src/utils/nsec3hash/nsec3hash_main.c index 8fa87a43c41fc5e71877de628f4622439913f174..5cfb8b938f09fa5df89bdb822774343d5cd446c6 100644 --- a/src/utils/nsec3hash/nsec3hash_main.c +++ b/src/utils/nsec3hash/nsec3hash_main.c @@ -150,10 +150,10 @@ int main(int argc, char *argv[]) fprintf(stderr, "Cannot transform IDN domain name.\n"); goto fail; } - dname = knot_dname_from_str(ascii_name); + dname = knot_dname_from_str_alloc(ascii_name); free(ascii_name); } else { - dname = knot_dname_from_str(argv[4]); + dname = knot_dname_from_str_alloc(argv[4]); } if (dname == NULL) { fprintf(stderr, "Cannot parse domain name.\n"); diff --git a/src/utils/nsupdate/nsupdate_exec.c b/src/utils/nsupdate/nsupdate_exec.c index 8b8cf397ccc047570038799afc68f5a666bc3502..c212d6987a33a08b2341e6c55d023b0b06397b8d 100644 --- a/src/utils/nsupdate/nsupdate_exec.c +++ b/src/utils/nsupdate/nsupdate_exec.c @@ -145,7 +145,7 @@ enum { static bool dname_isvalid(const char *lp) { - knot_dname_t *dn = knot_dname_from_str(lp); + knot_dname_t *dn = knot_dname_from_str_alloc(lp); if (dn == NULL) { return false; } @@ -187,7 +187,7 @@ static int parse_partial_rr(zs_scanner_t *s, const char *lp, unsigned flags) fqdn = false; } - knot_dname_t *owner = knot_dname_from_str(owner_str); + knot_dname_t *owner = knot_dname_from_str_alloc(owner_str); free(owner_str); if (owner == NULL) { return KNOT_EPARSEFAIL; @@ -388,7 +388,7 @@ static int build_query(nsupdate_params_t *params) /* Write question. */ knot_wire_set_id(query->wire, knot_random_uint16_t()); knot_wire_set_opcode(query->wire, KNOT_OPCODE_UPDATE); - knot_dname_t *qname = knot_dname_from_str(params->zone); + knot_dname_t *qname = knot_dname_from_str_alloc(params->zone); int ret = knot_pkt_put_question(query, qname, params->class_num, params->type_num); knot_dname_free(&qname, NULL); diff --git a/tests-extra/tests/events/soa/data/example.zone b/tests-extra/tests/events/soa/data/example.zone index 2d90e7dba83477f0dc8281b112994288c36245db..9f024246e9b3b22754d75051df84c9906ef5d311 100644 --- a/tests-extra/tests/events/soa/data/example.zone +++ b/tests-extra/tests/events/soa/data/example.zone @@ -1,7 +1,7 @@ $ORIGIN example. $TTL 3600 -@ SOA dns1 hostmaster 1 1 1 1 600 +@ SOA dns1 hostmaster 1 1 1 2 600 NS dns1 dns1 A 192.0.2.1 diff --git a/tests-extra/tests/events/soa/data/example.zone.1 b/tests-extra/tests/events/soa/data/example.zone.1 index 36a96d8db7677afd6c556b67d8e539a8696620fd..1657faf43b76f4418f96f671629916d913a4cc2d 100644 --- a/tests-extra/tests/events/soa/data/example.zone.1 +++ b/tests-extra/tests/events/soa/data/example.zone.1 @@ -1,7 +1,7 @@ $ORIGIN example. $TTL 3600 -@ SOA dns1 hostmaster 2 1 10 1 600 +@ SOA dns1 hostmaster 2 1 10 2 600 NS dns1 dns1 A 192.0.2.1 diff --git a/tests-extra/tests/events/soa/test.py b/tests-extra/tests/events/soa/test.py index d57565b2473fe558e86f35de53260007f2a2c5a0..37e976188c139c47c0bd84562aba8d8ccd965b95 100644 --- a/tests-extra/tests/events/soa/test.py +++ b/tests-extra/tests/events/soa/test.py @@ -4,6 +4,7 @@ from dnstest.utils import * from dnstest.test import Test +import random EXPIRE_SLEEP = 4 @@ -21,69 +22,92 @@ def test_expire(slave): resp = slave.dig("example.", "SOA") resp.check(rcode="SERVFAIL") +def test_run(t, action): + master = t.server("bind") + master.disable_notify = True + + slave = t.server("knot") + slave.disable_notify = True + slave.max_conn_idle = "1s" + + # this zone has refresh = 1s, retry = 1s and expire = 1s + 2s for connection timeouts + zone = t.zone("example.", storage=".") + + t.link(zone, master, slave) + t.start() + + slave.zone_wait(zone) + action(t, slave) # action should keep the event intact + #test that zone does not expire when master is alive + detail_log("Refresh - master alive") + test_refresh(slave) + master.stop() + #test that zone does expire when master is down + action(t, slave) # action should keep the event intact + detail_log("Refresh - master down") + test_expire(slave) + + #update master zone file with 10s retry in SOA + master.update_zonefile(zone, version=1) + master.start() + + slave.reload() #get new zone file + slave.zone_wait(zone) + #stop the master and start it again + master.stop() + t.sleep(EXPIRE_SLEEP) + master.start() + + #zone should expire, retry is pending now + detail_log("Retry - master dead then alive") + resp = slave.dig("example.", "SOA") + resp.check(rcode="SERVFAIL") + + #switch server roles, slave becomes master - there should be no expire + master.stop() + slave.zones = {} + master.zones = {} + t.link(zone, slave) + t.generate_conf() + action(t, slave) + + #test that the zone does not expire + slave.zone_wait(zone) + t.sleep(EXPIRE_SLEEP) + detail_log("Expire - roles switch") + slave.zone_wait(zone) + + #switch again - zone should expire now + slave.zones = {} + t.link(zone, master, slave) + t.generate_conf() + action(t, slave) + + detail_log("Expire - roles switch 2") + test_expire(slave) + + t.stop() + +def reload_server(t, s): + s.reload() + t.sleep(1) + +def restart_server(t, s): + s.stop() + s.start() + +def reload_or_restart(t, s): + if random.choice([True, False]): + restart_server(t, s) + else: + reload_server(t, s) + t = Test() -master = t.server("bind") -slave = t.server("knot") -slave.disable_notify = True -slave.max_conn_idle = "1s" - -# this zone has refresh = 1s, retry = 1s and expire = 1s + 2s for connection timeouts -zone = t.zone("example.", storage=".") - -t.link(zone, master, slave) - -t.start() - -slave.zone_wait(zone) -slave.reload() # reload should keep the event intact -#test that zone does not expire when master is alive -detail_log("Refresh - master alive") -test_refresh(slave) -master.stop() -#test that zone does expire when master is down -slave.reload() # reload should keep the event intact -detail_log("Refresh - master alive") -test_expire(slave) - -#update master zone file with 10s retry in SOA -master.update_zonefile(zone, version=1) -master.start() - -slave.reload() #get new zone file -slave.zone_wait(zone) -#stop the master and start it again -master.stop() -t.sleep(EXPIRE_SLEEP) -master.start() - -#zone should expire, retry is pending now -detail_log("Retry - master dead then alive") -resp = slave.dig("example.", "SOA") -resp.check(rcode="SERVFAIL") - -#switch server roles, slave becomes master - there should be no expire -master.stop() -slave.zones = {} -master.zones = {} -t.link(zone, slave) -t.generate_conf() -slave.reload() - -slave.zone_wait(zone) -t.sleep(EXPIRE_SLEEP) -detail_log("Expire - roles switch") -slave.zone_wait(zone) - -#switch again - zone should expire now -slave.zones = {} -t.link(zone, master, slave) -t.generate_conf() -slave.reload() -t.sleep(1) - -detail_log("Expire - roles switch 2") -test_expire(slave) - -t.stop() +random.seed() + +test_run(t, reload_server) +test_run(t, restart_server) +test_run(t, reload_or_restart) + diff --git a/tests/Makefile.am b/tests/Makefile.am index 74fff3bd17da996c70e0685e3309315c06bc7191..80beba65e8a70804d78b8931fb641b00256d83c4 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -45,7 +45,9 @@ check_PROGRAMS = \ worker_pool \ worker_queue \ zone_events \ - zone_update + zone_update \ + zone_timers \ + namedb check-compile-only: $(check_PROGRAMS) diff --git a/tests/changeset.c b/tests/changeset.c index e76da37344626cc1eeacce2abdc3609df9cda360..b9734dd0802f73c7c93d7493df59731c8ec5edb8 100644 --- a/tests/changeset.c +++ b/tests/changeset.c @@ -29,7 +29,7 @@ int main(int argc, char *argv[]) ok(changeset_empty(NULL), "changeset: NULL empty"); // Test creation. - knot_dname_t *d = knot_dname_from_str("test."); + knot_dname_t *d = knot_dname_from_str_alloc("test."); assert(d); changeset_t *ch = changeset_new(d); knot_dname_free(&d, NULL); @@ -41,7 +41,7 @@ int main(int argc, char *argv[]) ok(changeset_size(ch) == 0, "changeset: empty size"); // Test additions. - d = knot_dname_from_str("non.terminals.test."); + d = knot_dname_from_str_alloc("non.terminals.test."); assert(d); knot_rrset_t *apex_txt_rr = knot_rrset_new(d, KNOT_RRTYPE_TXT, KNOT_CLASS_IN, NULL); assert(apex_txt_rr); @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) // Add another node. knot_dname_free(&d, NULL); - d = knot_dname_from_str("here.come.more.non.terminals.test"); + d = knot_dname_from_str_alloc("here.come.more.non.terminals.test"); assert(d); knot_rrset_t *other_rr = knot_rrset_new(d, KNOT_RRTYPE_TXT, KNOT_CLASS_IN, NULL); assert(other_rr); @@ -114,14 +114,14 @@ int main(int argc, char *argv[]) // Create new changeset. knot_dname_free(&d, NULL); - d = knot_dname_from_str("test."); + d = knot_dname_from_str_alloc("test."); assert(d); changeset_t *ch2 = changeset_new(d); knot_dname_free(&d, NULL); assert(ch2); // Add something to add section. knot_dname_free(&apex_txt_rr->owner, NULL); - apex_txt_rr->owner = knot_dname_from_str("something.test."); + apex_txt_rr->owner = knot_dname_from_str_alloc("something.test."); assert(apex_txt_rr->owner); ret = changeset_add_rrset(ch2, apex_txt_rr); assert(ret == KNOT_EOK); @@ -129,7 +129,7 @@ int main(int argc, char *argv[]) // Add something to remove section. knot_dname_free(&apex_txt_rr->owner, NULL); apex_txt_rr->owner = - knot_dname_from_str("and.now.for.something.completely.different.test."); + knot_dname_from_str_alloc("and.now.for.something.completely.different.test."); assert(apex_txt_rr->owner); ret = changeset_rem_rrset(ch2, apex_txt_rr); assert(ret == KNOT_EOK); diff --git a/tests/conf.c b/tests/conf.c index 0b25aad27454f5c7145e4cce2892bbe6b46bb5fd..a70a756a677795cdae52cee252194a5826db556f 100644 --- a/tests/conf.c +++ b/tests/conf.c @@ -114,7 +114,7 @@ int main(int argc, char *argv[]) // Test 21: Load key dname const char *sample_str = "key0.example.net"; - knot_dname_t *sample = knot_dname_from_str(sample_str); + knot_dname_t *sample = knot_dname_from_str_alloc(sample_str); if (list_size(&conf->keys) > 0) { knot_tsig_key_t *k = &((conf_key_t *)HEAD(conf->keys))->k; ok(knot_dname_cmp(sample, k->name) == 0, diff --git a/tests/dname.c b/tests/dname.c index 4c7158a9c0ddb0cc103231387f1bf435dd9fe457..1b3d6df9b59fc410c3c50b8e8bc098ab3b9101bd 100644 --- a/tests/dname.c +++ b/tests/dname.c @@ -14,6 +14,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stdlib.h> #include <tap/basic.h> #include "libknot/dname.h" @@ -24,113 +25,472 @@ static int test_fw(size_t l, const char *w) { return knot_dname_wire_check((const uint8_t *)w, np, NULL) > 0; } +/* Test dname to/from string operations */ +static void test_str(const char *in_str, const char *in_bin, size_t bin_len) { + uint8_t d1[KNOT_DNAME_MAXLEN] = ""; + char s1[4 * KNOT_DNAME_MAXLEN] = ""; + knot_dname_t *d2 = NULL, *aux_d = NULL; + char *s2 = NULL, *aux_s = NULL; + int ret = 0; + + /* dname_from_str */ + aux_d = knot_dname_from_str(d1, in_str, sizeof(d1)); + ok(aux_d != NULL, "dname_from_str: %s", in_str); + if (aux_d == NULL) { + skip_block(10, "dname_from_str: %s", in_str); + return; + } + + /* dname_wire_check */ + ret = knot_dname_wire_check(d1, d1 + sizeof(d1), NULL); + ok(ret == bin_len, "dname_wire_check: %s", in_str); + + /* dname compare */ + ok(memcmp(d1, in_bin, bin_len) == 0, "dname compare: %s", in_str); + + /* dname_to_str */ + aux_s = knot_dname_to_str(s1, d1, sizeof(s1)); + ok(aux_s != NULL, "dname_to_str: %s", in_str); + if (aux_s == NULL) { + skip_block(7, "dname_to_str: %s", in_str); + return; + } + + /* dname_from_str_alloc */ + d2 = knot_dname_from_str_alloc(s1); + ok(d2 != NULL, "dname_from_str_alloc: %s", s1); + if (d2 == NULL) { + skip_block(6, "dname_from_str_alloc: %s", s1); + return; + } + + /* dname_wire_check */ + ret = knot_dname_wire_check(d2, d2 + bin_len, NULL); + ok(ret == bin_len, "dname_wire_check: %s", s1); + + /* dname compare */ + ok(d2 && memcmp(d2, in_bin, bin_len) == 0, "dname compare: %s", s1); + + /* dname_to_str_alloc */ + s2 = knot_dname_to_str_alloc(d2); + knot_dname_free(&d2, NULL); + ok(s2 != NULL, "dname_to_str_alloc: %s", s1); + if (s2 == NULL) { + skip_block(3, "dname_to_str_alloc: %s", s1); + return; + } + + /* As the string representation is ambiguous, the following steps + * are just for comparison in wire form. + */ + d2 = knot_dname_from_str_alloc(s2); + ok(d2 != NULL, "dname_from_str_alloc: %s", s2); + if (aux_d == NULL) { + skip_block(2, "dname_from_str_alloc: %s", s2); + free(s2); + return; + } + + /* dname_wire_check */ + ret = knot_dname_wire_check(d2, d2 + bin_len, NULL); + ok(ret == bin_len, "dname_wire_check: %s", s2); + + /* dname compare */ + ok(d2 && memcmp(d2, in_bin, bin_len) == 0, "dname compare: %s", s2); + + knot_dname_free(&d2, NULL); + free(s2); +} + int main(int argc, char *argv[]) { - plan(29); - + plan(285); + knot_dname_t *d = NULL, *d2 = NULL; const char *w = NULL, *t = NULL; unsigned len = 0; size_t pos = 0; + char *s = NULL; + + /* DNAME WIRE CHECKS */ - /* 1. NULL wire */ + /* NULL wire */ ok(!test_fw(0, NULL), "parsing NULL dname"); - /* 2. empty label */ + /* empty label */ ok(test_fw(1, ""), "parsing empty dname"); - /* 3. incomplete dname */ - ok(!test_fw(5, "\x08""dddd"), "parsing incomplete wire"); + /* incomplete dname */ + ok(!test_fw(5, "\x08" "dddd"), "parsing incomplete wire"); - /* 4. non-fqdn */ - ok(!test_fw(3, "\x02""ab"), "parsing non-fqdn name"); + /* non-fqdn */ + ok(!test_fw(3, "\x02" "ab"), "parsing non-fqdn name"); - /* 5. label > 63b */ - w = "\x40""dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"; - ok(!test_fw(65, w), "parsing label > 63b"); + /* label length == 63 */ + w = "\x3f" "123456789012345678901234567890123456789012345678901234567890123"; + ok(test_fw(1 + 63 + 1, w), "parsing label length == 63"); - /* 6. label count == 126 */ - w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"; - ok(test_fw(253, w), "parsing label count == 127"); + /* label length > 63 */ + w = "\x40" "1234567890123456789012345678901234567890123456789012345678901234"; + ok(!test_fw(1 + 64 + 1, w), "parsing label length > 63"); - /* 7. label count == 127 */ - w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"; - ok(test_fw(255, w), "parsing label count == 127"); + /* label count == 127 (also maximal dname length) */ + w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"; + ok(test_fw(127 * 2 + 1, w), "parsing label count == 127"); - /* 8. label count > 127 */ - w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"; - ok(!test_fw(257, w), "parsing label count > 127"); + /* label count > 127 */ + w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"; + ok(!test_fw(128 * 2 + 1, w), "parsing label count > 127"); - /* 9. dname length > 255 */ - w = "\xff""ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"; - ok(!test_fw(257, w), "parsing dname len > 255"); + /* dname length > 255 */ + w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x02\x64\x64"; + ok(!test_fw(126 * 2 + 3 + 1, w), "parsing dname len > 255"); - /* 10. special case - invalid label */ - w = "\x20\x68\x6d\x6e\x63\x62\x67\x61\x61\x61\x61\x65\x72\x6b\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x67\x6e\x69\x64\x68\x62\x61\x61\x61\x61\x65\x6c\x64\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x61\x63\x6f\x63\x64\x62\x61\x61\x61\x61\x65\x6b\x72\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x69\x62\x63\x6d\x6a\x6f\x61\x61\x61\x61\x65\x72\x6a\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x6f\x6c\x6e\x6c\x67\x68\x61\x61\x61\x61\x65\x73\x72\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x6a\x6b\x64\x66\x66\x67\x61\x61\x61\x61\x65\x6c\x68\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x67\x67\x6c\x70\x70\x61\x61\x61\x61\x61\x65\x73\x72\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x61\x61\x61\x62\x65\x6a\x61\x6d\x20\x65\x6b\x6c\x67\x70\x66\x61\x61\x61\x61\x65\x6c\x68\x30\x30\x30\x30\x64\x6c\x61\x61\x61\x61\x61\x0\x21\x42\x63\x84\xa5\xc6\xe7\x8\xa\xd\x11\x73\x3\x6e\x69\x63\x2\x43\x5a"; - ok(!test_fw(277, w), "parsing invalid label (spec. case 1)"); + /* DNAME STRING CHECKS */ - /* 11. parse from string (correct) .*/ - len = 10; - w = "\x04""abcd""\x03""efg"; - t = "abcd.efg"; - d = knot_dname_from_str(t); - ok(d && knot_dname_size(d) == len && memcmp(d, w, len) == 0, - "dname_fromstr: parsed correct non-FQDN name"); - knot_dname_free(&d, NULL); + /* root dname */ + test_str(".", "\x00", 1); - /* 12. parse FQDN from string (correct) .*/ - t = "abcd.efg."; - d = knot_dname_from_str(t); - ok(d && knot_dname_size(d) == len && memcmp(d, w, len) == 0, - "dname_fromstr: parsed correct FQDN name"); - knot_dname_free(&d, NULL); + /* 1-char dname */ + test_str("a.", "\x01""a", 2 + 1); + + /* 1-char dname - non-fqdn */ + test_str("a", "\x01""a", 2 + 1); + + /* wildcard and asterisks */ + test_str("*.*a.a*a.**.", + "\x01" "*" "\x02" "*a" "\x03" "a*a" "\x02" "**", + 2 + 3 + 4 + 3 + 1); + + /* special label */ + test_str("\\000\\0320\\ \\\\\\\"\\.\\@\\*.", + "\x09" "\x00\x20\x30\x20\x5c\x22.@*", + 10 + 1); + + /* unescaped special characters */ + test_str("_a.b-c./d.", + "\x02" "_a" "\x03" "b-c" "\x02" "/d", + 3 + 4 + 3 + 1); + + /* all possible characters */ + test_str("\\000\\001\\002\\003\\004\\005\\006\\007\\008\\009\\010\\011\\012\\013\\014\\015\\016\\017\\018\\019", + "\x14" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13", + 22); + test_str("\\020\\021\\022\\023\\024\\025\\026\\027\\028\\029\\030\\031\\032\\033\\034\\035\\036\\037\\038\\039", + "\x14" "\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27", + 22); + test_str("\\040\\041\\042\\043\\044\\045\\046\\047\\048\\049\\050\\051\\052\\053\\054\\055\\056\\057\\058\\059", + "\x14" "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b", + 22); + test_str("\\060\\061\\062\\063\\064\\065\\066\\067\\068\\069\\070\\071\\072\\073\\074\\075\\076\\077\\078\\079", + "\x14" "\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f", + 22); + test_str("\\080\\081\\082\\083\\084\\085\\086\\087\\088\\089\\090\\091\\092\\093\\094\\095\\096\\097\\098\\099", + "\x14" "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63", + 22); + test_str("\\100\\101\\102\\103\\104\\105\\106\\107\\108\\109\\110\\111\\112\\113\\114\\115\\116\\117\\118\\119", + "\x14" "\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77", + 22); + test_str("\\120\\121\\122\\123\\124\\125\\126\\127\\128\\129\\130\\131\\132\\133\\134\\135\\136\\137\\138\\139", + "\x14" "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b", + 22); + test_str("\\140\\141\\142\\143\\144\\145\\146\\147\\148\\149\\150\\151\\152\\153\\154\\155\\156\\157\\158\\159", + "\x14" "\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f", + 22); + test_str("\\160\\161\\162\\163\\164\\165\\166\\167\\168\\169\\170\\171\\172\\173\\174\\175\\176\\177\\178\\179", + "\x14" "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3", + 22); + test_str("\\180\\181\\182\\183\\184\\185\\186\\187\\188\\189\\190\\191\\192\\193\\194\\195\\196\\197\\198\\199", + "\x14" "\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7", + 22); + test_str("\\200\\201\\202\\203\\204\\205\\206\\207\\208\\209\\210\\211\\212\\213\\214\\215\\216\\217\\218\\219", + "\x14" "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb", + 22); + test_str("\\220\\221\\222\\223\\224\\225\\226\\227\\228\\229\\230\\231\\232\\233\\234\\235\\236\\237\\238\\239", + "\x14" "\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef", + 22); + test_str("\\240\\241\\242\\243\\244\\245\\246\\247\\248\\249\\250\\251\\252\\253\\254\\255", + "\x10" "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + 18); + + /* maximal dname label length */ + test_str("12345678901234567890123456789012345678901234567890123456789012\\063", + "\x3f" "12345678901234567890123456789012345678901234567890123456789012?", + 65); + + /* maximal dname length */ + test_str("1234567890123456789012345678901234567890123456789." + "1234567890123456789012345678901234567890123456789." + "1234567890123456789012345678901234567890123456789." + "1234567890123456789012345678901234567890123456789." + "\\#234567890123456789012345678901234567890123456789012\\063", + "\x31" "1234567890123456789012345678901234567890123456789" + "\x31" "1234567890123456789012345678901234567890123456789" + "\x31" "1234567890123456789012345678901234567890123456789" + "\x31" "1234567890123456789012345678901234567890123456789" + "\x35" "#234567890123456789012345678901234567890123456789012?", + 255); + + /* NULL output, positive maxlen */ + w = "\x02" "aa"; + s = knot_dname_to_str(NULL, (const uint8_t *)w, 1); + ok(s != NULL, "dname_to_str: null dname"); + if (s != NULL) { + ok(memcmp(s, "aa.", 4) == 0, "dname_to_str: null dname compare"); + free(s); + } else { + skip("dname_to_str: null dname"); + } + + /* non-NULL output, zero maxlen */ + char s_small[2]; + s = knot_dname_to_str(s_small, (const uint8_t *)w, 0); + ok(s == NULL, "dname_to_str: non-NULL output, zero maxlen"); + + /* small buffer */ + s = knot_dname_to_str(s_small, (const uint8_t *)w, 1); + ok(s == NULL, "dname_to_str: small buffer"); + + /* NULL dname */ + s = knot_dname_to_str_alloc(NULL); + ok(s == NULL, "dname_to_str: null dname"); - /* 13. parse name from string (incorrect) .*/ + /* empty dname is considered as a root dname */ + w = ""; + s = knot_dname_to_str_alloc((const uint8_t *)w); + ok(s != NULL, "dname_to_str: empty dname"); + if (s != NULL) { + ok(memcmp(s, ".", 1) == 0, "dname_to_str: empty dname is root dname"); + free(s); + } else { + skip("dname_to_str: empty dname"); + } + + /* incomplete dname */ + w = "\x08" "dddd"; + s = knot_dname_to_str_alloc((const uint8_t *)w); + ok(s != NULL, "dname_to_str: incomplete dname"); + free(s); + + /* non-fqdn */ + w = "\x02" "ab"; + s = knot_dname_to_str_alloc((const uint8_t *)w); + ok(s != NULL, "dname_to_str: non-fqdn"); + free(s); + + /* label length > 63 */ + w = "\x40" "1234567890123456789012345678901234567890123456789012345678901234"; + s = knot_dname_to_str_alloc((const uint8_t *)w); + ok(s != NULL, "dname_to_str: label length > 63"); + free(s); + + /* label count > 127 */ + w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"; + s = knot_dname_to_str_alloc((const uint8_t *)w); + ok(s != NULL, "dname_to_str: label count > 127"); + free(s); + + /* dname length > 255 */ + w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64" + "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x02\x64\x64"; + s = knot_dname_to_str_alloc((const uint8_t *)w); + ok(s != NULL, "dname_to_str: dname length > 255"); + free(s); + + /* NULL output, positive maxlen */ + s = "aa."; + d = knot_dname_from_str(NULL, s, 1); + ok(s != NULL, "dname_from_str: null name"); + if (s != NULL) { + ok(memcmp(d, "\x02" "aa", 4) == 0, "dname_from_str: null name compare"); + free(d); + } else { + skip("dname_from_str: null name"); + } + + /* non-NULL output, zero maxlen */ + uint8_t d_small[2]; + d = knot_dname_from_str(d_small, s, 0); + ok(d == NULL, "dname_from_str: non-NULL output, zero maxlen"); + + /* small buffer */ + d = knot_dname_from_str(d_small, s, 1); + ok(d == NULL, "dname_from_str: small buffer"); + + /* NULL string */ + d = knot_dname_from_str_alloc(NULL); + ok(d == NULL, "dname_from_str: null string"); + + /* empty string */ + t = ""; + d = knot_dname_from_str_alloc(t); + ok(d == NULL, "dname_from_str: empty string"); + + /* empty label */ t = ".."; - d = knot_dname_from_str(t); - ok(d == NULL, "dname_fromstr: parsed incorrect name"); + d = knot_dname_from_str_alloc(t); + ok(d == NULL, "dname_from_str: empty label"); + + /* leading dot */ + t = ".a"; + d = knot_dname_from_str_alloc(t); + ok(d == NULL, "dname_from_str: leading dot"); + + /* incomplete decimal notation I */ + t = "\\1"; + d = knot_dname_from_str_alloc(t); + ok(d == NULL, "dname_from_str: incomplete decimal I"); + + /* incomplete decimal notation II */ + t = "\\12"; + d = knot_dname_from_str_alloc(t); + ok(d == NULL, "dname_from_str: incomplete decimal II"); + + /* invalid decimal notation I */ + t = "\\256"; + d = knot_dname_from_str_alloc(t); + ok(d == NULL, "dname_from_str: invalid decimal I"); + + /* invalid decimal notation II */ + t = "\\2x6"; + d = knot_dname_from_str_alloc(t); + ok(d == NULL, "dname_from_str: invalid decimal II"); - /* 14. equal name is subdomain */ + /* invalid escape notation */ + t = "\\2"; + d = knot_dname_from_str_alloc(t); + ok(d == NULL, "dname_from_str: invalid escape"); + + /* label length > 63 I */ + t = "1234567890123456789012345678901234567890123456789012345678901234"; + d = knot_dname_from_str_alloc(t); + ok(d == NULL, "dname_from_str: label length > 63 I"); + + /* label length > 63 II */ + t = "123456789012345678901234567890123456789012345678901234567890123\\?"; + d = knot_dname_from_str_alloc(t); + ok(d == NULL, "dname_from_str: label length > 63 II"); + + /* label length > 63 III */ + t = "123456789012345678901234567890123456789012345678901234567890123\\063"; + d = knot_dname_from_str_alloc(t); + ok(d == NULL, "dname_from_str: label length > 63 III"); + + /* dname length > 255 */ + t = "1234567890123456789012345678901234567890123456789." + "1234567890123456789012345678901234567890123456789." + "1234567890123456789012345678901234567890123456789." + "1234567890123456789012345678901234567890123456789." + "123456789012345678901234567890123456789012345678901234.", + d = knot_dname_from_str_alloc(t); + ok(d == NULL, "dname_from_str: dname length > 255"); + + /* DNAME SUBDOMAIN CHECKS */ + + /* equal name is subdomain */ t = "ab.cd.ef"; - d2 = knot_dname_from_str(t); + d2 = knot_dname_from_str_alloc(t); t = "ab.cd.ef"; - d = knot_dname_from_str(t); + d = knot_dname_from_str_alloc(t); ok(!knot_dname_is_sub(d, d2), "dname_subdomain: equal name"); knot_dname_free(&d, NULL); - /* 15. true subdomain */ + /* true subdomain */ t = "0.ab.cd.ef"; - d = knot_dname_from_str(t); + d = knot_dname_from_str_alloc(t); ok(knot_dname_is_sub(d, d2), "dname_subdomain: true subdomain"); knot_dname_free(&d, NULL); - /* 16. not subdomain */ + /* not subdomain */ t = "cd.ef"; - d = knot_dname_from_str(t); + d = knot_dname_from_str_alloc(t); ok(!knot_dname_is_sub(d, d2), "dname_subdomain: not subdomain"); knot_dname_free(&d, NULL); - /* 17. root subdomain */ + /* root subdomain */ t = "."; - d = knot_dname_from_str(t); + d = knot_dname_from_str_alloc(t); ok(knot_dname_is_sub(d2, d), "dname_subdomain: root subdomain"); knot_dname_free(&d, NULL); knot_dname_free(&d2, NULL); - /* 18-19. dname cat (valid) */ + /* DNAME CAT CHECKS */ + + /* dname cat (valid) */ w = "\x03""cat"; d = knot_dname_copy((const uint8_t *)w, NULL); t = "*"; - d2 = knot_dname_from_str(t); + d2 = knot_dname_from_str_alloc(t); d2 = knot_dname_cat(d2, d); t = "\x01""*""\x03""cat"; len = 2 + 4 + 1; - ok (d2 && len == knot_dname_size(d2), "dname_cat: valid concatenation size"); - ok(memcmp(d2, t, len) == 0, "dname_cat: valid concatenation"); + ok(d2 && len == knot_dname_size(d2), "dname_cat: valid concatenation size"); + ok(d2 && memcmp(d2, t, len) == 0, "dname_cat: valid concatenation"); knot_dname_free(&d, NULL); knot_dname_free(&d2, NULL); - /* 20-21. parse from wire (valid) */ + /* DNAME PARSE CHECKS */ + + /* parse from wire (valid) */ t = "\x04""abcd""\x03""efg"; len = 10; pos = 0; @@ -139,7 +499,7 @@ int main(int argc, char *argv[]) ok(pos == len, "dname_parse: valid name (parsed length)"); knot_dname_free(&d, NULL); - /* 22-23. parse from wire (invalid) */ + /* parse from wire (invalid) */ t = "\x08""dddd"; len = 5; pos = 0; @@ -147,30 +507,38 @@ int main(int argc, char *argv[]) ok(d == NULL, "dname_parse: bad name"); ok(pos == 0, "dname_parse: bad name (parsed length)"); - /* name equality checks */ + /* DNAME EQUALITY CHECKS */ + t = "ab.cd.ef"; - d = knot_dname_from_str(t); + d = knot_dname_from_str_alloc(t); ok(knot_dname_is_equal(d, d), "dname_is_equal: equal names"); + t = "ab.cd.fe"; - d2 = knot_dname_from_str(t); + d2 = knot_dname_from_str_alloc(t); ok(!knot_dname_is_equal(d, d2), "dname_is_equal: same label count"); knot_dname_free(&d2, NULL); + t = "ab.cd"; - d2 = knot_dname_from_str(t); + d2 = knot_dname_from_str_alloc(t); ok(!knot_dname_is_equal(d, d2), "dname_is_equal: len(d1) < len(d2)"); knot_dname_free(&d2, NULL); + t = "ab.cd.ef.gh"; - d2 = knot_dname_from_str(t); + d2 = knot_dname_from_str_alloc(t); ok(!knot_dname_is_equal(d, d2), "dname_is_equal: len(d1) > len(d2)"); knot_dname_free(&d2, NULL); + t = "ab.cd.efe"; - d2 = knot_dname_from_str(t); + d2 = knot_dname_from_str_alloc(t); ok(!knot_dname_is_equal(d, d2), "dname_is_equal: last label longer"); knot_dname_free(&d2, NULL); + t = "ab.cd.e"; - d2 = knot_dname_from_str(t); + d2 = knot_dname_from_str_alloc(t); ok(!knot_dname_is_equal(d, d2), "dname_is_equal: last label shorter"); knot_dname_free(&d2, NULL); + knot_dname_free(&d, NULL); + return 0; } diff --git a/tests/dnssec_keys.c b/tests/dnssec_keys.c index 8dbb91924cbea06be3a99694210546cd2ba70b1f..b2b048c65a2f7fb7b36772aec4c4c3501f6b3829 100644 --- a/tests/dnssec_keys.c +++ b/tests/dnssec_keys.c @@ -205,7 +205,7 @@ int main(int argc, char *argv[]) knot_key_params_t params = { 0 }; knot_tsig_key_t tsig_key = { 0 }; const char *owner = "shared.example.com."; - knot_dname_t *name = knot_dname_from_str(owner); + knot_dname_t *name = knot_dname_from_str_alloc(owner); result = knot_tsig_key_from_params(¶ms, &tsig_key); ok(result == KNOT_EINVAL, diff --git a/tests/dnssec_nsec3.c b/tests/dnssec_nsec3.c index 17dbb8e8777c853dad27f4c3db851bba1f0f5f6f..4e5dfe4b50b7c3cea4590d93bbbd5533b20eb2fd 100644 --- a/tests/dnssec_nsec3.c +++ b/tests/dnssec_nsec3.c @@ -54,7 +54,7 @@ int main(int argc, char *argv[]) 'a', 'b', 'c', 'd' // salt }; - knot_dname_t *owner = knot_dname_from_str("test."); + knot_dname_t *owner = knot_dname_from_str_alloc("test."); rrset = knot_rrset_new(owner, KNOT_RRTYPE_NSEC3PARAM, KNOT_CLASS_IN, NULL); knot_dname_free(&owner, NULL); @@ -81,7 +81,7 @@ int main(int argc, char *argv[]) params.salt = (uint8_t *)strdup("happywithnsec3"); const char *dname_str = "knot-dns.cz."; - knot_dname_t *dname = knot_dname_from_str(dname_str); + knot_dname_t *dname = knot_dname_from_str_alloc(dname_str); uint8_t expected[] = { 0x72, 0x40, 0x55, 0x83, 0x92, 0x93, 0x95, 0x28, 0xee, 0xa2, diff --git a/tests/dnssec_sign.c b/tests/dnssec_sign.c index 5941c0ffeceb20072536a4bb3328fb52836b8836..abe6e2f34f93e8fcbefd4a7270e562bb246cf939 100644 --- a/tests/dnssec_sign.c +++ b/tests/dnssec_sign.c @@ -93,7 +93,7 @@ int main(int argc, char *argv[]) // RSA - kp.name = knot_dname_from_str("example.com."); + kp.name = knot_dname_from_str_alloc("example.com."); kp.algorithm = 5; knot_binary_from_base64("pSxiFXG8wB1SSHdok+OdaAp6QdvqjpZ17ucNge21iYVfv+DZq52l21KdmmyEqoG9wG/87O7XG8XVLNyYPue8Mw==", &kp.modulus); knot_binary_from_base64("AQAB", &kp.public_exponent); @@ -109,7 +109,7 @@ int main(int argc, char *argv[]) // DSA - kp.name = knot_dname_from_str("example.com."); + kp.name = knot_dname_from_str_alloc("example.com."); kp.algorithm = 6; knot_binary_from_base64("u7tr4jc7CH0+r2muVEZyjYu7hpMrQ1dHGAMv7hr5dBFYzkutfdBmDSW4C+qxaXWo14gi+jJ8XqFqQ7rQn23DdQ==", &kp.prime); knot_binary_from_base64("tgZ5X6pFoCOM2NzfiAYVG1434Mk=", &kp.subprime); @@ -123,7 +123,7 @@ int main(int argc, char *argv[]) // ECDSA #ifdef KNOT_ENABLE_ECDSA - kp.name = knot_dname_from_str("example.com"); + kp.name = knot_dname_from_str_alloc("example.com"); kp.algorithm = 13; knot_binary_from_base64("1N/PvpB8jZcvv+zr3Q987RKK1cBxDKULzEc5F/nnpSg=", &kp.private_key); knot_binary_from_base64("AAAAAH3t6EfkvHK5fQMGslhWcCfMF6Q3oNbol2f19DGAb8r49ZX7iQ12sFIyrs2CiwDxFR9Y7fF2zOZ005VV1LA3m1Q=", &kp.rdata); @@ -135,7 +135,7 @@ int main(int argc, char *argv[]) #endif #if KNOT_ENABLE_GOST - kp.name = knot_dname_from_str("example.com"); + kp.name = knot_dname_from_str_alloc("example.com"); kp.algorithm = 12; knot_binary_from_base64("MEUCAQAwHAYGKoUDAgITMBIGByqFAwICIwEGByqFAwICHgEEIgIgN2CMRL538HmFM9+GHYM54rEDYO+tLDV3q7AtK1nZ4iA=", &kp.private_key); knot_binary_from_base64("eHh4eOJ4YHvlasoDRc4ZnvRzldoTUgwWSW0bPv7r9xJ074Dn8KzM4yU9fJgTwIT1TsaHmejYopDnVdjxZyrKNra8Keo=", &kp.rdata); diff --git a/tests/dnssec_zone_nsec.c b/tests/dnssec_zone_nsec.c index f3f578c20d4959a89c143c921f8461d23bc3826a..5b2a0750cbfe0d1501ff203c31f19d07972e77fb 100644 --- a/tests/dnssec_zone_nsec.c +++ b/tests/dnssec_zone_nsec.c @@ -23,9 +23,9 @@ int main(int argc, char *argv[]) { plan(1); - knot_dname_t *owner = knot_dname_from_str("name.example.com"); - knot_dname_t *apex = knot_dname_from_str("example.com"); - knot_dname_t *expect = knot_dname_from_str("sv9o5lv8kgv6lm1t9dkst43b3c0aagbj.example.com"); + knot_dname_t *owner = knot_dname_from_str_alloc("name.example.com"); + knot_dname_t *apex = knot_dname_from_str_alloc("example.com"); + knot_dname_t *expect = knot_dname_from_str_alloc("sv9o5lv8kgv6lm1t9dkst43b3c0aagbj.example.com"); knot_nsec3_params_t params = { .algorithm = 1, .flags = 0, .iterations = 10, diff --git a/tests/namedb.c b/tests/namedb.c new file mode 100644 index 0000000000000000000000000000000000000000..f008a5be924fa17f39ba92aac8eb6d3dcd2d20c2 --- /dev/null +++ b/tests/namedb.c @@ -0,0 +1,176 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <time.h> +#include <tap/basic.h> + +#include "libknot/common.h" +#include "common/mempool.h" +#include "common/mem.h" +#include "common/namedb/namedb_lmdb.h" +#include "common/namedb/namedb_trie.h" + +/* Constants. */ +#define KEY_MAXLEN 64 +#define KEY_SET(key, str) key.data = (str); key.len = strlen(str) + 1 + +/*! \brief Generate random key. */ +static const char *alphabet = "abcdefghijklmn0123456789"; +static char *str_key_rand(size_t len, mm_ctx_t *pool) +{ + char *s = mm_alloc(pool, len); + memset(s, 0, len); + for (unsigned i = 0; i < len - 1; ++i) { + s[i] = alphabet[rand() % strlen(alphabet)]; + } + return s; +} + +/* UCW array sorting defines. */ +#define ASORT_PREFIX(X) str_key_##X +#define ASORT_KEY_TYPE char* +#define ASORT_LT(x, y) (strcmp((x), (y)) < 0) +#include "common-knot/array-sort.h" + +static void namedb_test_set(unsigned nkeys, char **keys, char *dbid, + struct namedb_api *api, mm_ctx_t *pool) +{ + if (api == NULL) { + skip("API not compiled in"); + return; + } + + /* Create database */ + knot_namedb_t *db = api->init(dbid, pool); + ok(db != NULL, "%s: create", api->name); + + /* Start WR transaction. */ + knot_txn_t txn; + int ret = api->txn_begin(db, &txn, 0); + ok(ret == KNOT_EOK, "%s: txn_begin(WR)", api->name); + + /* Insert keys */ + knot_val_t key, val; + bool passed = true; + for (unsigned i = 0; i < nkeys; ++i) { + KEY_SET(key, keys[i]); + val.len = sizeof(void*); + val.data = &key.data; + + ret = api->insert(&txn, &key, &val, 0); + if (ret != KNOT_EOK && ret != KNOT_EEXIST) { + passed = false; + break; + } + } + ok(passed, "%s: insert", api->name); + + /* Commit WR transaction. */ + ret = api->txn_commit(&txn); + ok(ret == KNOT_EOK, "%s: txn_commit(WR)", api->name); + + /* Start RD transaction. */ + ret = api->txn_begin(db, &txn, KNOT_NAMEDB_RDONLY); + ok(ret == KNOT_EOK, "%s: txn_begin(RD)", api->name); + + /* Lookup all keys */ + passed = true; + for (unsigned i = 0; i < nkeys; ++i) { + KEY_SET(key, keys[i]); + + ret = api->find(&txn, &key, &val, 0); + if (ret != KNOT_EOK) { + passed = false; + break; + } + + const char **stored_key = val.data; + if (strcmp(*stored_key, keys[i]) != 0) { + diag("%s: mismatch on element '%u'", api->name, i); + passed = false; + break; + } + } + ok(passed, "%s: lookup all keys", api->name); + + /* Fetch dataset size. */ + int db_size = api->count(&txn); + ok(db_size > 0 && db_size <= nkeys, "%s: count %d", api->name, db_size); + + /* Unsorted iteration */ + int iterated = 0; + knot_iter_t *it = api->iter_begin(&txn, 0); + while (it != NULL) { + ++iterated; + it = api->iter_next(it); + } + api->iter_finish(it); + is_int(db_size, iterated, "%s: unsorted iteration", api->name); + + /* Sorted iteration. */ + char key_buf[KEY_MAXLEN] = {'\0'}; + iterated = 0; + it = api->iter_begin(&txn, KNOT_NAMEDB_SORTED); + while (it != NULL) { + ret = api->iter_key(it, &key); + if (iterated > 0) { /* Only if previous exists. */ + if (strcmp(key_buf, key.data) > 0) { + diag("%s: iter_sort '%s' <= '%s' FAIL\n", + api->name, key_buf, (const char *)key.data); + break; + } + } + ++iterated; + memcpy(key_buf, key.data, key.len); + it = api->iter_next(it); + } + is_int(db_size, iterated, "hattrie: sorted iteration"); + api->iter_finish(it); + + api->txn_abort(&txn); + api->deinit(db); +} + +int main(int argc, char *argv[]) +{ + plan_lazy(); + + mm_ctx_t pool; + mm_ctx_mempool(&pool, 4096); + + /* Temporary DB identifier. */ + char dbid_buf[] = "/tmp/namedb.XXXXXX"; + char *dbid = mkdtemp(dbid_buf); + + /* Random keys. */ + unsigned nkeys = 10000; + char **keys = mm_alloc(&pool, sizeof(char*) * nkeys); + for (unsigned i = 0; i < nkeys; ++i) { + keys[i] = str_key_rand(KEY_MAXLEN, &pool); + } + + /* Sort random keys. */ + str_key_sort(keys, nkeys); + + /* Execute test set for all backends. */ + namedb_test_set(nkeys, keys, dbid, namedb_lmdb_api(), &pool); + namedb_test_set(nkeys, keys, dbid, namedb_trie_api(), &pool); + + /* Cleanup */ + mp_delete(pool.ctx); + return 0; +} diff --git a/tests/node.c b/tests/node.c index 3d527721d8a6dabb34a1a4eefd0aa38575577ac8..9f75eab1b2edf6e9e233457d12eda23c4696144f 100644 --- a/tests/node.c +++ b/tests/node.c @@ -50,7 +50,7 @@ int main(int argc, char *argv[]) { plan(23); - knot_dname_t *dummy_owner = knot_dname_from_str("test."); + knot_dname_t *dummy_owner = knot_dname_from_str_alloc("test."); // Test new zone_node_t *node = node_new(dummy_owner, NULL); ok(node != NULL, "Node: new"); diff --git a/tests/pkt.c b/tests/pkt.c index 3a0d9b36db3654bd1f9761cc74bbe266ab48746a..cb596a13faac13eef8233959035846f7b042e86e 100644 --- a/tests/pkt.c +++ b/tests/pkt.c @@ -76,7 +76,7 @@ int main(int argc, char *argv[]) knot_dname_t* dnames[NAMECOUNT] = {0}; knot_rrset_t* rrsets[NAMECOUNT] = {0}; for (unsigned i = 0; i < NAMECOUNT; ++i) { - dnames[i] = knot_dname_from_str(g_names[i]); + dnames[i] = knot_dname_from_str_alloc(g_names[i]); } uint8_t *edns_str = (uint8_t *)"ab"; diff --git a/tests/rrl.c b/tests/rrl.c index 3b8899de1086f2831084956099cdb96ee1a7644c..dcb7e9635ede8ad1a8b48417059342c18bb361ab 100644 --- a/tests/rrl.c +++ b/tests/rrl.c @@ -102,7 +102,7 @@ int main(int argc, char *argv[]) return KNOT_ERROR; /* Fatal */ } - knot_dname_t *qname = knot_dname_from_str("beef."); + knot_dname_t *qname = knot_dname_from_str_alloc("beef."); int ret = knot_pkt_put_question(query, qname, KNOT_CLASS_IN, KNOT_RRTYPE_A); knot_dname_free(&qname, NULL); if (ret != KNOT_EOK) { diff --git a/tests/rrset.c b/tests/rrset.c index 1849a19fe790aeffeffec0b4a133c9ca315bf9ea..8c2192214984ef3549d4317e6321aadaded0c05d 100644 --- a/tests/rrset.c +++ b/tests/rrset.c @@ -39,7 +39,7 @@ int main(int argc, char *argv[]) plan(19); // Test new - knot_dname_t *dummy_owner = knot_dname_from_str("test."); + knot_dname_t *dummy_owner = knot_dname_from_str_alloc("test."); assert(dummy_owner); knot_rrset_t *rrset = knot_rrset_new(dummy_owner, KNOT_RRTYPE_TXT, @@ -52,7 +52,7 @@ int main(int argc, char *argv[]) // Test init knot_dname_free(&dummy_owner, NULL); - dummy_owner = knot_dname_from_str("test2."); + dummy_owner = knot_dname_from_str_alloc("test2."); assert(dummy_owner); knot_dname_free(&rrset->owner, NULL); diff --git a/tests/zone_events.c b/tests/zone_events.c index a011f887e559e0ecf7a631e3157ec179ab40a4ba..804cf08f5c3045d568d97aad3184ba0d8c467ebb 100644 --- a/tests/zone_events.c +++ b/tests/zone_events.c @@ -18,7 +18,7 @@ #include "common-knot/evsched.h" #include "knot/worker/pool.h" -#include "knot/zone/events.h" +#include "knot/zone/events/events.h" #include "knot/zone/zone.h" static void test_scheduling(zone_t *zone) diff --git a/tests/zone_timers.c b/tests/zone_timers.c new file mode 100644 index 0000000000000000000000000000000000000000..3162806f1d11b5bf5155cf6f1f2e9108d34f2e92 --- /dev/null +++ b/tests/zone_timers.c @@ -0,0 +1,123 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <time.h> +#include <tap/basic.h> + +#include "libknot/common.h" +#include "common/namedb/namedb.h" +#include "common/namedb/namedb_lmdb.h" +#include "knot/zone/timers.h" +#include "knot/zone/zone.h" +#include "knot/zone/events/events.h" + +#define SLIP (1024 * 1024) +static const size_t REFRESH_SLIP = SLIP; +static const size_t EXPIRE_SLIP = SLIP + 1; +static const size_t FLUSH_SLIP = SLIP + 2; + +int main(int argc, char *argv[]) +{ + plan_lazy(); + + if (namedb_lmdb_api() == NULL) { + skip("LMDB API not compiled"); + return EXIT_SUCCESS; + } + + // Temporary DB identifier. + char dbid_buf[] = "/tmp/timerdb.XXXXXX"; + const char *dbid = mkdtemp(dbid_buf); + + // Mockup zones. + conf_zone_t zone_conf = { .name = "test1." }; + zone_t *zone_1 = zone_new(&zone_conf); + zone_conf.name = "test2."; + zone_t *zone_2 = zone_new(&zone_conf); + assert(zone_1 && zone_2); + + // Mockup zonedb. + knot_zonedb_t *zone_db = knot_zonedb_new(2); + assert(zone_db); + int ret = knot_zonedb_insert(zone_db, zone_1); + assert(ret == KNOT_EOK); + ret = knot_zonedb_insert(zone_db, zone_2); + assert(ret == KNOT_EOK); + + knot_namedb_t *db = open_timers_db(dbid); + ok(db != NULL, "zone timers: create"); + + // Set up events in the future. + const time_t now = time(NULL); + const time_t REFRESH_TIME = now + REFRESH_SLIP; + const time_t EXPIRE_TIME = now + EXPIRE_SLIP; + const time_t FLUSH_TIME = now + FLUSH_SLIP; + + // Refresh, expire and flush are the permanent events for now. + zone_events_schedule_at(zone_1, ZONE_EVENT_REFRESH, REFRESH_TIME); + zone_events_schedule_at(zone_1, ZONE_EVENT_EXPIRE, EXPIRE_TIME); + zone_events_schedule_at(zone_1, ZONE_EVENT_FLUSH, FLUSH_TIME); + + // Write the timers. + ret = write_zone_timers(db, zone_1); + ok(ret == KNOT_EOK, "zone timers: write"); + + // Read the timers. + time_t timers[ZONE_EVENT_COUNT]; + ret = read_zone_timers(db, zone_1, timers); + ok(ret == KNOT_EOK && + timers[ZONE_EVENT_REFRESH] == REFRESH_TIME && + timers[ZONE_EVENT_EXPIRE] == EXPIRE_TIME && + timers[ZONE_EVENT_FLUSH] == FLUSH_TIME, "zone timers: read set"); + + // Sweep and read again - timers should stay the same. + int s_ret = sweep_timer_db(db, zone_db); + if (s_ret == KNOT_EOK) { + ret = read_zone_timers(db, zone_1, timers); + } + ok(s_ret == KNOT_EOK && ret == KNOT_EOK && + timers[ZONE_EVENT_REFRESH] == REFRESH_TIME && + timers[ZONE_EVENT_EXPIRE] == EXPIRE_TIME && + timers[ZONE_EVENT_FLUSH] == FLUSH_TIME, "zone timers: sweep no-op"); + + // Read timers for unset zone. + const time_t empty_timers[ZONE_EVENT_COUNT] = { '\0' }; + ret = read_zone_timers(db, zone_2, timers); + ok(ret == KNOT_EOK && + memcmp(timers, empty_timers, sizeof(timers)) == 0, "zone timers: read unset"); + + // Remove first zone from db and sweep. + ret = knot_zonedb_del(zone_db, zone_1->name); + assert(ret == KNOT_EOK); + + s_ret = sweep_timer_db(db, zone_db); + if (s_ret == KNOT_EOK) { + ret = read_zone_timers(db, zone_1, timers); + } + ok(s_ret == KNOT_EOK && ret == KNOT_EOK && + memcmp(timers, empty_timers, sizeof(timers)) == 0, "zone timers: sweep"); + + // Clean up. + zone_1->conf = NULL; + zone_2->conf = NULL; + zone_free(&zone_1); + zone_free(&zone_2); + close_timers_db(db); + + return EXIT_SUCCESS; +} + diff --git a/tests/zone_update.c b/tests/zone_update.c index 5553b8e6463d25d758b6cfcda8186459a6e95b43..cf596543fea94a0f394e0cb158f40e9723965b39 100644 --- a/tests/zone_update.c +++ b/tests/zone_update.c @@ -59,7 +59,7 @@ int main(int argc, char *argv[]) { plan(5); - knot_dname_t *apex = knot_dname_from_str("test"); + knot_dname_t *apex = knot_dname_from_str_alloc("test"); assert(apex); zone_contents_t *zone = zone_contents_new(apex); knot_dname_free(&apex, NULL); diff --git a/tests/zonedb.c b/tests/zonedb.c index 2670d733ea8ed48a0f532b299d154ea1322ae135..cc0ed69ab3d69bdfb045986564bbb27694a9fa4d 100644 --- a/tests/zonedb.c +++ b/tests/zonedb.c @@ -72,7 +72,7 @@ int main(int argc, char *argv[]) /* Lookup of exact names. */ nr_passed = 0; for (unsigned i = 0; i < ZONE_COUNT; ++i) { - dname = knot_dname_from_str(zone_list[i]); + dname = knot_dname_from_str_alloc(zone_list[i]); if (knot_zonedb_find(db, dname) == zones[i]) { ++nr_passed; } else { @@ -89,7 +89,7 @@ int main(int argc, char *argv[]) if (strcmp(zone_list[i], ".") != 0) { strlcat(buf, zone_list[i], sizeof(buf)); } - dname = knot_dname_from_str(buf); + dname = knot_dname_from_str_alloc(buf); if (knot_zonedb_find_suffix(db, dname) == zones[i]) { ++nr_passed; } else { @@ -102,7 +102,7 @@ int main(int argc, char *argv[]) /* Remove all zones. */ nr_passed = 0; for (unsigned i = 0; i < ZONE_COUNT; ++i) { - dname = knot_dname_from_str(zone_list[i]); + dname = knot_dname_from_str_alloc(zone_list[i]); if (knot_zonedb_del(db, dname) == KNOT_EOK) { zone_free(&zones[i]); ++nr_passed; diff --git a/tests/ztree.c b/tests/ztree.c index 4d24d9cd5c2848d2f310240490923f26d9a807bc..0edbc7ecd5480534c6985c8cb8190e66638ee2a3 100644 --- a/tests/ztree.c +++ b/tests/ztree.c @@ -24,10 +24,10 @@ static zone_node_t NODE[NCOUNT]; static knot_dname_t* ORDER[NCOUNT]; static void ztree_init_data() { - NAME[0] = knot_dname_from_str("."); - NAME[1] = knot_dname_from_str("master.ac."); - NAME[2] = knot_dname_from_str("ac."); - NAME[3] = knot_dname_from_str("ns."); + NAME[0] = knot_dname_from_str_alloc("."); + NAME[1] = knot_dname_from_str_alloc("master.ac."); + NAME[2] = knot_dname_from_str_alloc("ac."); + NAME[3] = knot_dname_from_str_alloc("ns."); knot_dname_t *order[NCOUNT] = { NAME[0], NAME[2], NAME[1], NAME[3] @@ -55,8 +55,8 @@ static int ztree_iter_data(zone_node_t **node, void *data) int result = KNOT_EOK; if (owner != ORDER[*i]) { result = KNOT_ERROR; - char *exp_s = knot_dname_to_str(ORDER[*i]); - char *owner_s = knot_dname_to_str(owner); + char *exp_s = knot_dname_to_str_alloc(ORDER[*i]); + char *owner_s = knot_dname_to_str_alloc(owner); diag("ztree: at index: %u expected '%s' got '%s'\n", *i, exp_s, owner_s); free(exp_s); free(owner_s); @@ -103,7 +103,7 @@ int main(int argc, char *argv[]) /* 4. ordered lookup */ node = NULL; const zone_node_t *prev = NULL; - knot_dname_t *tmp_dn = knot_dname_from_str("z.ac."); + knot_dname_t *tmp_dn = knot_dname_from_str_alloc("z.ac."); zone_tree_find_less_or_equal(t, tmp_dn, &node, &prev); knot_dname_free(&tmp_dn, NULL); ok(prev == NODE + 1, "ztree: ordered lookup");