diff --git a/Knot.files b/Knot.files index 75eb05fd11d04d4dde8fe574c3e1b51f510fa098..506a37141684e300c519bd2e85469b97b9c54fc8 100644 --- a/Knot.files +++ b/Knot.files @@ -41,8 +41,6 @@ src/common-knot/hex.c src/common-knot/hex.h src/common-knot/hhash.c src/common-knot/hhash.h -src/common-knot/print.c -src/common-knot/print.h src/common-knot/ref.c src/common-knot/ref.h src/common-knot/slab/alloc-common.h @@ -69,6 +67,8 @@ src/common/mem.c src/common/mem.h src/common/mempool.c src/common/mempool.h +src/common/print.c +src/common/print.h src/common/sockaddr.c src/common/sockaddr.h src/common/strlcpy.c @@ -164,6 +164,8 @@ src/knot/updates/changesets.c src/knot/updates/changesets.h src/knot/updates/ddns.c src/knot/updates/ddns.h +src/knot/updates/zone-update.c +src/knot/updates/zone-update.h src/knot/worker/pool.c src/knot/worker/pool.h src/knot/worker/queue.c @@ -228,6 +230,8 @@ src/libknot/packet/net.h src/libknot/packet/pkt.c src/libknot/packet/pkt.h src/libknot/packet/wire.h +src/libknot/processing/overlay.c +src/libknot/processing/overlay.h src/libknot/processing/process.c src/libknot/processing/process.h src/libknot/processing/requestor.c @@ -240,6 +244,7 @@ src/libknot/rrset-dump.c src/libknot/rrset-dump.h src/libknot/rrset.c src/libknot/rrset.h +src/libknot/rrtype/aaaa.h src/libknot/rrtype/dnskey.h src/libknot/rrtype/nsec.h src/libknot/rrtype/nsec3.c @@ -291,8 +296,6 @@ src/zscanner/error.c src/zscanner/error.h src/zscanner/functions.c src/zscanner/functions.h -src/zscanner/loader.c -src/zscanner/loader.h src/zscanner/scanner.h src/zscanner/scanner.rl src/zscanner/scanner_body.rl @@ -301,7 +304,6 @@ src/zscanner/tests/processing.h src/zscanner/tests/tests.c src/zscanner/tests/tests.h src/zscanner/tests/zscanner-tool.c -src/zscanner/zscanner.h tests/Makefile.am tests/acl.c tests/base32hex.c @@ -322,6 +324,7 @@ tests/hattrie.c tests/hhash.c tests/journal.c tests/node.c +tests/overlay.c tests/pkt.c tests/process_answer.c tests/process_query.c @@ -337,10 +340,7 @@ tests/slab.c tests/wire.c tests/worker_pool.c tests/worker_queue.c +tests/zone-update.c tests/zone_events.c tests/zonedb.c tests/ztree.c -src/libknot/rrtype/aaaa.h -src/libknot/processing/overlay.h -src/libknot/processing/overlay.c -tests/overlay.c diff --git a/doc/configuration.rst b/doc/configuration.rst index 60ebbbf4ece8e931b58262327265d4a5eb3c4c13..ebd085e1f362b954735267f2775c894eefb6fe6e 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -271,8 +271,6 @@ The signing keys can be generated using ISC ``dnssec-keygen`` tool only and there are some limitations: * Keys for all zones must be placed in one directory. -* Algorithms based on RSA, DSA, and ECDSA are supported, support for - GOST algorithm is not finished yet. * Only key publication, activation, inactivation, and removal time stamps are utilized. Other time stamps are ignored. * It is required, that both ``.private`` and ``.key`` files for each @@ -280,6 +278,7 @@ only and there are some limitations: (even for verification only). * There cannot be more than eight keys per zone. Keys which are not published are not included in this number. +* Single-Type Signing Scheme is not supported. Example how to generate NSEC3 capable zone signing key (ZSK) and key signing key (KSK) for zone ``example.com``:: diff --git a/doc/running.rst b/doc/running.rst index f740666aa4e420ab309fc6f7e4166143522b8a19..496834ed95496e1155272213b85ee02b4a603241 100644 --- a/doc/running.rst +++ b/doc/running.rst @@ -51,13 +51,13 @@ If you want to control the daemon directly, use ``SIGINT`` to quit the process o Actions: stop Stop server. reload Reload configuration and changed zones. - refresh [zone] Refresh slave zone (all if not specified). + refresh <zone> Refresh slave zone (all if not specified). flush Flush journal and update zone files. status Check if server is running. zonestatus Show status of configured zones. checkconf Check current server configuration. - checkzone [zone] Check zone (all if not specified). - memstats [zone] Estimate memory consumption for zone (all if not + checkzone <zone> Check zone (all if not specified). + memstats <zone> Estimate memory consumption for zone (all if not specified). Also, the server needs to create several files in order to run properly. These diff --git a/src/Makefile.am b/src/Makefile.am index d7e3f4de0ad580e1e15734d74b31fdddd6f69443..ff7e3ddc2bb812546ce38263254ae26d4c04ff0f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -289,6 +289,8 @@ libknotd_la_SOURCES = \ knot/updates/ddns.h \ knot/updates/apply.c \ knot/updates/apply.h \ + knot/updates/zone-update.c \ + knot/updates/zone-update.h \ knot/worker/pool.c \ knot/worker/pool.h \ knot/worker/queue.c \ diff --git a/src/common-knot/hattrie/hat-trie.c b/src/common-knot/hattrie/hat-trie.c index a2b4dcb015bd23dfb72e2092c9632bfc0e317112..9f208569d25a8e910012988c7c5b5e27ffd4d2eb 100644 --- a/src/common-knot/hattrie/hat-trie.c +++ b/src/common-knot/hattrie/hat-trie.c @@ -80,7 +80,7 @@ static trie_node_t* alloc_trie_node(hattrie_t* T, node_ptr child) for (i = 0; i < NODE_CHILDS; ++i) node->xs[i] = child; return node; } - + /* iterate trie nodes until string is consumed or bucket is found */ static node_ptr hattrie_consume_ns(node_ptr **s, size_t *sp, size_t slen, const char **k, size_t *l, unsigned min_len) @@ -190,6 +190,39 @@ static value_t* hattrie_find_rightmost(node_ptr node) return hhash_indexval(node.b, node.b->weight - 1); } +static value_t* hattrie_find_leftmost(node_ptr node) +{ + /* iterate children from left */ + if (*node.flag & NODE_TYPE_TRIE) { + for (int i = 0; i <= TRIE_MAXCHAR; ++i) { + /* skip repeated pointers to hybrid bucket */ + if (i < TRIE_MAXCHAR - 1 && node.t->xs[i].t == node.t->xs[i + 1].t) { + continue; + } + /* nest if trie */ + value_t *ret = hattrie_find_leftmost(node.t->xs[i]); + if (ret) { + return ret; + } + } + /* use trie node value if no children found */ + if (node.t->flag & NODE_HAS_VAL) { + return &node.t->val; + } + + /* no non-empty children? */ + return NULL; + } + + /* node is hashtable */ + if (node.b->weight == 0) { + return NULL; + } + /* return leftmost value */ + assert(node.b->index); + return hhash_indexval(node.b, 0); +} + /* find node in trie and keep node stack (if slen > 0) */ static node_ptr hattrie_find_ns(node_ptr **s, size_t *sp, size_t slen, const char **key, size_t *len) @@ -665,7 +698,7 @@ value_t* hattrie_tryget(hattrie_t* T, const char* key, size_t len) return hhash_find(node.b, key, len); } -static value_t* hattrie_walk(node_ptr* s, size_t sp, +static value_t* hattrie_walk_left(node_ptr* s, size_t sp, const char* key, value_t* (*f)(node_ptr)) { value_t *r = NULL; @@ -701,6 +734,41 @@ static value_t* hattrie_walk(node_ptr* s, size_t sp, return NULL; } +static value_t* hattrie_walk_right(node_ptr* s, size_t sp, + const char* key, value_t* (*f)(node_ptr)) +{ + value_t *r = NULL; + while (r == NULL) { + /* if not found next in table, it should be + * the leftmost of the nodes right of the current + */ + node_ptr visited = s[sp].t->xs[(unsigned char)*key]; + for (int i = *key + 1; i <= TRIE_MAXCHAR; ++i) { + if (s[sp].t->xs[i].flag == visited.flag) + continue; /* skip pointers to visited container */ + r = f(s[sp].t->xs[i]); + if (r) { + return r; + } + } + /* use trie node value if possible */ + if (s[sp].t->flag & NODE_HAS_VAL) { + return &s[sp].t->val; + } + + /* consumed whole stack */ + if (sp == 0) { + break; + } + + /* pop stack */ + --key; + --sp; + } + + return NULL; +} + int hattrie_find_leq (hattrie_t* T, const char* key, size_t len, value_t** dst) { /* create node stack for traceback */ @@ -713,7 +781,7 @@ int hattrie_find_leq (hattrie_t* T, const char* key, size_t len, value_t** dst) int ret = 1; /* no node on the left matches */ node_ptr node = hattrie_find_ns(&ns, &sp, NODESTACK_INIT, &key, &len); if (node.flag == NULL) { - *dst = hattrie_walk(ns, sp, key, hattrie_find_rightmost); + *dst = hattrie_walk_left(ns, sp, key, hattrie_find_rightmost); if (ns != bs) free(ns); if (*dst) { return -1; /* found previous */ @@ -741,7 +809,7 @@ int hattrie_find_leq (hattrie_t* T, const char* key, size_t len, value_t** dst) --key; } /* walk up the stack of visited nodes and find closest match on the left */ - *dst = hattrie_walk(ns, sp, key, hattrie_find_rightmost); + *dst = hattrie_walk_left(ns, sp, key, hattrie_find_rightmost); if (*dst) { ret = -1; /* found previous */ } else { @@ -753,6 +821,49 @@ int hattrie_find_leq (hattrie_t* T, const char* key, size_t len, value_t** dst) return ret; } +int hattrie_find_next (hattrie_t* T, const char* key, size_t len, value_t **dst) +{ + /* create node stack for traceback */ + size_t sp = 0; + node_ptr bs[NODESTACK_INIT]; /* base stack (will be enough mostly) */ + node_ptr *ns = bs; /* generic ptr, could point to new mem */ + ns[sp] = T->root; + + /* find node for given key */ + node_ptr ptr = hattrie_find_ns(&ns, &sp, NODESTACK_INIT, &key, &len); + if (ptr.flag == NULL) { + *dst = hattrie_walk_right(ns, sp, key, hattrie_find_leftmost); + if (*dst) { + return 0; /* found next. */ + } else { + return 1; /* no next key found. */ + } + } + + int ret = 0; + if (*ptr.flag & NODE_TYPE_TRIE) { + /* Get next using walk. */ + ret = 1; + } else { + ret = hhash_find_next(ptr.b, key, len, dst); + } + + if (ret == 1) { + /* we're retracing from pure bucket, pop the key. */ + if (*ptr.flag & NODE_TYPE_PURE_BUCKET) { + --key; + } + *dst = hattrie_walk_right(ns, sp, key, hattrie_find_leftmost); + if (*dst) { + ret = 0; /* found next. */ + } else { + ret = 1; /* no next key found. */ + } + } + + return ret; +} + int hattrie_del(hattrie_t* T, const char* key, size_t len) { node_ptr parent = T->root; diff --git a/src/common-knot/hattrie/hat-trie.h b/src/common-knot/hattrie/hat-trie.h index 0de47d5d45a083005972e9405cfe82858cb7bd57..a188ec93612e0aa56c91bb9e694f6317d921768f 100644 --- a/src/common-knot/hattrie/hat-trie.h +++ b/src/common-knot/hattrie/hat-trie.h @@ -73,6 +73,8 @@ value_t* hattrie_tryget (hattrie_t*, const char* key, size_t len); /** Find a given key in the table, returning a NULL pointer if it does not * exist. Also set prev to point to previous node. */ int hattrie_find_leq (hattrie_t*, const char* key, size_t len, value_t** dst); +/** Find a next value for given key, returning NULL if it does not exist. */ +int hattrie_find_next (hattrie_t* T, const char* key, size_t len, value_t **dst); /** Delete a given key from trie. Returns 0 if successful or -1 if not found. */ diff --git a/src/common-knot/hhash.c b/src/common-knot/hhash.c index 4c2051afd064a40789fc4941086840b943eed089..6a5211207858e76b615002261e24285054ee78b6 100644 --- a/src/common-knot/hhash.c +++ b/src/common-knot/hhash.c @@ -432,6 +432,25 @@ int hhash_find_leq(hhash_t* tbl, const char* key, uint16_t len, value_t** dst) return 1; } +int hhash_find_next(hhash_t* tbl, const char* key, uint16_t len, value_t** dst) +{ + *dst = NULL; + if (tbl->weight == 0) { + return 1; + } + + int k = BIN_SEARCH_FIRST_GE_CMP(tbl, tbl->weight, CMP_LE, key, len); + hhelem_t *found = tbl->item + tbl->index[k]; + /* Found prev or equal, we want next */ + if (k + 1 < tbl->weight) { + found = tbl->item + tbl->index[k + 1]; + *dst = (value_t *)KEY_VAL(found->d); + return 0; + } else { + return 1; + } +} + /* Private iterator flags. */ enum { HH_SORTED = 0x01 /* sorted iteration */ diff --git a/src/common-knot/hhash.h b/src/common-knot/hhash.h index 5c1d8f472a466ea5d8470f6c7569531527d43dca..d57fb4ec85c71bb3e7af47764b1346d2607525c5 100644 --- a/src/common-knot/hhash.h +++ b/src/common-knot/hhash.h @@ -169,6 +169,14 @@ void hhash_build_index(hhash_t* tbl); */ int hhash_find_leq(hhash_t* tbl, const char* key, uint16_t len, value_t **dst); +/*! + * \brief Find a key that is a lexicographic successor. + * + * \retval 0 if successor found. + * \retval 1 if couldn't find a successor. + */ +int hhash_find_next(hhash_t* tbl, const char* key, uint16_t len, value_t** dst); + /*! \brief Hash table iterator. */ typedef struct htable_iter { unsigned flags; /* Internal */ diff --git a/src/common/log.c b/src/common/log.c index 87694050aeb6a5a0a1323486953673b16244a2e6..26f9953562f7b26e48331baa8f5737afbfb97882 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -107,7 +107,8 @@ static struct log_sink *sink_setup(unsigned logfiles) /*! \brief Publish new log sink and free the replaced. */ static void sink_publish(struct log_sink *log) { - struct log_sink *old_log = rcu_xchg_pointer(&s_log, log); + struct log_sink **current_log = &s_log; + struct log_sink *old_log = rcu_xchg_pointer(current_log, log); synchronize_rcu(); sink_free(old_log); } @@ -277,7 +278,7 @@ static int emit_log_msg(logsrc_t src, int level, const char *msg) } // Print - ret = fprintf(stream, "%s%s", tstr, msg); + ret = fprintf(stream, "%s%s\n", tstr, msg); if (stream == stdout) { fflush(stream); } @@ -481,7 +482,7 @@ int log_reconfigure(const struct conf_t *conf, void *data) if (facility == LOGT_FILE) { facility = log_open_file(log, facility_conf->file); if (facility < 0) { - log_error("Failed to open logfile '%s'\n", + log_error("Failed to open logfile '%s'", facility_conf->file); continue; } diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c index 520b89ecb12f7b6d561ce7dd22a4275c2cdec179..5c3119e8a4b8dad08181bfcbcb11d75211644c88 100644 --- a/src/knot/conf/conf.c +++ b/src/knot/conf/conf.c @@ -84,7 +84,7 @@ static void cf_print_error(void *scanner, const char *msg) filename = new_config->filename; } - log_error("config error in '%s' (line %d token '%s'): %s\n", + log_error("config error in '%s' (line %d token '%s'): %s", filename, lineno, text, msg); _parser_res = KNOT_EPARSEFAIL; @@ -226,7 +226,7 @@ static int conf_process(conf_t *conf) /* Check for ACL existence. */ if (!EMPTY_LIST(conf->ctl.allow)) { log_warning("control 'allow' statement does not " - "affect UNIX sockets\n"); + "affect UNIX sockets"); } } else if (sockaddr_port(&ctl_if->addr) <= 0) { sockaddr_port_set(&ctl_if->addr, REMOTE_DPORT); @@ -365,7 +365,7 @@ static int conf_process(conf_t *conf) ) { log_zone_str_notice(zone->name, "automatic " "DNSSEC signing enabled, disabling " - "incoming XFRs\n"); + "incoming XFRs"); WALK_LIST_FREE(zone->acl.notify_in); WALK_LIST_FREE(zone->acl.xfr_in); @@ -393,13 +393,13 @@ static int conf_process(conf_t *conf) /* Check paths existence. */ if (!is_existing_dir(zone->storage)) { - log_error("storage directory '%s' does not exist\n", + log_error("storage directory '%s' does not exist", zone->storage); ret = KNOT_EINVAL; continue; } if (zone->dnssec_enable && !is_existing_dir(zone->dnssec_keydir)) { - log_error("DNSSEC key directory '%s' does not exist\n", + log_error("DNSSEC key directory '%s' does not exist", zone->dnssec_keydir); ret = KNOT_EINVAL; continue; @@ -820,8 +820,9 @@ char* strcpath(char *path) char* remainder = strchr(path,'~'); if (remainder != NULL) { if (remainder[1] != '/') { - log_warning("cannot expand non-login user home" - "directory '%s', use full path instead", path); + log_warning("cannot expand non-login user home " + "directory '%s', use full path instead", + path); } // Get full path diff --git a/src/knot/ctl/estimator.c b/src/knot/ctl/estimator.c index 80d0df1392511bdb9394ed134ec16e054aed1ae0..c948c1ed97371c65d53474bb77ac126ca8329ed0 100644 --- a/src/knot/ctl/estimator.c +++ b/src/knot/ctl/estimator.c @@ -21,7 +21,6 @@ #include "common/lists.h" #include "common/mem.h" #include "knot/zone/node.h" -#include "zscanner/zscanner.h" #include "libknot/descriptor.h" // Addition constants used for tweaking, mostly malloc overhead diff --git a/src/knot/ctl/estimator.h b/src/knot/ctl/estimator.h index 96167ed3c40a07888c2b9adb340acaefc0579cd2..8060c9b1073cd8b1311c71b9b487005a2c339047 100644 --- a/src/knot/ctl/estimator.h +++ b/src/knot/ctl/estimator.h @@ -27,7 +27,7 @@ #pragma once #include "common-knot/hattrie/hat-trie.h" -#include "zscanner/zscanner.h" +#include "zscanner/scanner.h" // Mutiplicative constant, needed because of malloc's fragmentation static const double ESTIMATE_MAGIC = 1.0; diff --git a/src/knot/ctl/knotc_main.c b/src/knot/ctl/knotc_main.c index 265572286967825b7ceeac853fc9e59122342752..522466de4203c349349c7aacd94e4b8669b66e2e 100644 --- a/src/knot/ctl/knotc_main.c +++ b/src/knot/ctl/knotc_main.c @@ -80,14 +80,14 @@ static int cmd_signzone(int argc, char *argv[], unsigned flags); knot_cmd_t knot_cmd_tbl[] = { {&cmd_stop, 0, "stop", "", "\t\tStop server."}, {&cmd_reload, 0, "reload", "", "\tReload configuration and changed zones."}, - {&cmd_refresh, 0, "refresh", "[zone]", "\tRefresh slave zone (all if not specified). Flag '-f' forces retransfer."}, + {&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_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."}, - {&cmd_checkzone, 1, "checkzone", "[zone]", "Check zone (all if not specified)."}, - {&cmd_memstats, 1, "memstats", "[zone]", "Estimate memory use for zone (all if not specified)."}, - {&cmd_signzone, 0, "signzone", "[zone]", "Sign all zones with available DNSSEC keys."}, + {&cmd_checkzone, 1, "checkzone", "<zone>", "Check zone (all if not specified)."}, + {&cmd_memstats, 1, "memstats", "<zone>", "Estimate memory use for zone (all if not specified)."}, + {&cmd_signzone, 0, "signzone", "<zone>", "Sign all zones with available DNSSEC keys."}, {NULL, 0, NULL, NULL, NULL} }; @@ -183,14 +183,14 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[]) /* Check remote address. */ conf_iface_t *r = conf()->ctl.iface; if (!r || r->addr.ss_family == AF_UNSPEC) { - log_error("no remote address for '%s' configured\n", cmd); + log_error("no remote address for '%s' configured", cmd); return 1; } /* Make query. */ knot_pkt_t *pkt = remote_query(cmd, r->key); if (!pkt) { - log_warning("could not prepare query for '%s'\n", cmd); + log_warning("could not prepare query for '%s'", cmd); return 1; } @@ -200,7 +200,7 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[]) knot_rrset_t rr; int res = remote_build_rr(&rr, "data.", rrt); if (res != KNOT_EOK) { - log_error("couldn't create the query\n"); + log_error("couldn't create the query"); knot_pkt_free(&pkt); return 1; } @@ -217,7 +217,7 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[]) } res = knot_pkt_put(pkt, 0, &rr, KNOT_PF_FREE); if (res != KNOT_EOK) { - log_error("couldn't create the query\n"); + log_error("couldn't create the query"); knot_rrset_clear(&rr, NULL); knot_pkt_free(&pkt); return 1; @@ -227,7 +227,7 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[]) if (r->key) { int res = remote_query_sign(pkt->wire, &pkt->size, pkt->max_size, r->key); if (res != KNOT_EOK) { - log_error("couldn't sign the packet\n"); + log_error("couldn't sign the packet"); knot_pkt_free(&pkt); return 1; } @@ -241,7 +241,7 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[]) int s = net_connected_socket(SOCK_STREAM, &r->addr, &r->via, 0); if (s < 0) { - log_error("couldn't connect to remote host '%s'\n", addr_str); + log_error("couldn't connect to remote host '%s'", addr_str); knot_pkt_free(&pkt); return 1; } @@ -249,7 +249,7 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[]) /* Wait for availability. */ struct pollfd pfd = { s, POLLOUT, 0 }; if (poll(&pfd, 1, conf()->max_conn_reply) != 1) { - log_error("couldn't connect to remote host '%s'\n", addr_str); + log_error("couldn't connect to remote host '%s'", addr_str); close(s); knot_pkt_free(&pkt); return 1; @@ -261,7 +261,7 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[]) /* Evaluate and wait for reply. */ if (ret <= 0) { - log_error("couldn't connect to remote host '%s'\n", addr_str); + log_error("couldn't connect to remote host '%s'", addr_str); close(s); return 1; } @@ -272,7 +272,7 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[]) ret = cmd_remote_reply(s); if (ret != KNOT_EOK) { if (ret != KNOT_ECONN) { - log_warning("remote command reply: %s\n", + log_warning("remote command reply: %s", knot_strerror(ret)); rc = 1; } @@ -373,7 +373,7 @@ static int tsig_parse_file(knot_tsig_key_t *k, const char *f) { FILE* fp = fopen(f, "r"); if (!fp) { - log_error("couldn't open key-file '%s'\n", f); + log_error("couldn't open key-file '%s'", f); return KNOT_EINVAL; } @@ -394,7 +394,7 @@ static int tsig_parse_file(knot_tsig_key_t *k, const char *f) if (c == '\n') { if (k->name) { log_error("only one key definition allowed " - "in '%s'\n", f); + "in '%s'", f); ret = KNOT_EMALF; break; } @@ -455,14 +455,14 @@ int main(int argc, char **argv) case 'y': if (tsig_parse_str(&r_key, optarg) != KNOT_EOK) { rc = 1; - log_error("couldn't parse TSIG key '%s'\n", optarg); + log_error("couldn't parse TSIG key '%s'", optarg); goto exit; } break; case 'k': if (tsig_parse_file(&r_key, optarg) != KNOT_EOK) { rc = 1; - log_error("couldn't parse TSIG key file '%s'\n", optarg); + log_error("couldn't parse TSIG key file '%s'", optarg); goto exit; } break; @@ -512,7 +512,7 @@ int main(int argc, char **argv) /* Command not found. */ if (!cmd->name) { - log_error("invalid command: '%s'\n", argv[optind]); + log_error("invalid command: '%s'", argv[optind]); rc = 1; goto exit; } @@ -525,7 +525,7 @@ int main(int argc, char **argv) /* Check if config file is required. */ if (has_flag(flags, F_NOCONF) && cmd->need_conf) { - log_error("couldn't find a config file, refusing to continue\n"); + log_error("couldn't find a config file, refusing to continue"); rc = 1; goto exit; } @@ -654,11 +654,40 @@ static int cmd_checkconf(int argc, char *argv[], unsigned flags) UNUSED(argv); UNUSED(flags); - log_info("OK, configuration is valid\n"); + log_info("OK, configuration is valid"); return 0; } +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); + if (zone_name == NULL) { + return false; + } + (void)knot_dname_to_lower(zone_name); + + bool found = false; + + int i = 0; + while (!found && i < argc) { + /* Convert the argument to dname */ + knot_dname_t *arg_name = knot_dname_from_str(argv[i]); + + if (arg_name != NULL) { + (void)knot_dname_to_lower(arg_name); + found = knot_dname_is_equal(zone_name, arg_name); + } + + i++; + knot_dname_free(&arg_name, NULL); + } + + knot_dname_free(&zone_name, NULL); + return found; +} + static int cmd_checkzone(int argc, char *argv[], unsigned flags) { UNUSED(flags); @@ -676,19 +705,7 @@ static int cmd_checkzone(int argc, char *argv[], unsigned flags) conf_zone_t *zone = (conf_zone_t *)*hattrie_iter_val(z_iter); /* Fetch zone */ - int zone_match = 0; - for (unsigned i = 0; i < (unsigned)argc; ++i) { - size_t len = strlen(zone->name); - - /* All (except root) without final dot */ - if (len > 1) { - len -= 1; - } - if (strncmp(zone->name, argv[i], len) == 0) { - zone_match = 1; - break; - } - } + int zone_match = fetch_zone(argc, argv, zone); if (!zone_match && argc > 0) { conf_free_zone(zone); @@ -702,7 +719,7 @@ static int cmd_checkzone(int argc, char *argv[], unsigned flags) continue; } - log_zone_str_info(zone->name, "zone is OK\n"); + log_zone_str_info(zone->name, "zone is OK"); zone_contents_deep_free(&loaded_zone); } hattrie_iter_free(z_iter); @@ -715,7 +732,6 @@ static int cmd_memstats(int argc, char *argv[], unsigned flags) UNUSED(flags); /* Zone checking */ - int rc = 0; double total_size = 0; /* Generate databases for all zones */ @@ -728,19 +744,7 @@ static int cmd_memstats(int argc, char *argv[], unsigned flags) conf_zone_t *zone = (conf_zone_t *)*hattrie_iter_val(z_iter); /* Fetch zone */ - int zone_match = 0; - for (unsigned i = 0; i < (unsigned)argc; ++i) { - size_t len = strlen(zone->name); - - /* All (except root) without final dot */ - if (len > 1) { - len -= 1; - } - if (strncmp(zone->name, argv[i], len) == 0) { - zone_match = 1; - break; - } - } + int zone_match = fetch_zone(argc, argv, zone); if (!zone_match && argc > 0) { conf_free_zone(zone); @@ -759,31 +763,29 @@ static int cmd_memstats(int argc, char *argv[], unsigned flags) .htable_size = 0, .rdata_size = 0, .record_count = 0 }; if (est.node_table == NULL) { - log_error("not enough memory\n"); - continue; + log_error("not enough memory"); + break; } - /* Create file loader. */ - zs_loader_t *loader = zs_loader_create(zone->file, zone->name, - KNOT_CLASS_IN, 3600, - estimator_rrset_memsize_wrap, - process_error, - &est); - if (loader == NULL) { - rc = 1; - log_zone_str_error(zone->name, "could not load zone\n"); + /* Create zone scanner. */ + zs_scanner_t *zs = zs_scanner_create(zone->name, + KNOT_CLASS_IN, 3600, + estimator_rrset_memsize_wrap, + process_error, + &est); + if (zs == NULL) { + log_zone_str_error(zone->name, "could not load zone"); hattrie_free(est.node_table); continue; } /* Do a parser run, but do not actually create the zone. */ - int ret = zs_loader_process(loader); - if (ret != KNOT_EOK) { - rc = 1; - log_zone_str_error(zone->name, "failed to parse zone\n"); + int ret = zs_scanner_parse_file(zs, zone->file); + if (ret != 0) { + log_zone_str_error(zone->name, "failed to parse zone"); hattrie_apply_rev(est.node_table, estimator_free_trie_node, NULL); hattrie_free(est.node_table); - zs_loader_free(loader); + zs_scanner_free(zs); continue; } @@ -801,18 +803,18 @@ static int cmd_memstats(int argc, char *argv[], unsigned flags) est.htable_size + malloc_size) * ESTIMATE_MAGIC) / (1024.0 * 1024.0); - log_zone_str_info(zone->name, "%zu RRs, used memory estimation is %zu MB\n", + log_zone_str_info(zone->name, "%zu RRs, used memory estimation is %zu MB", est.record_count, (size_t)zone_size); - zs_loader_free(loader); + zs_scanner_free(zs); total_size += zone_size; conf_free_zone(zone); } hattrie_iter_free(z_iter); - if (rc == 0 && argc == 0) { // for all zones - log_info("estimated memory consumption for all zones is %zu MB\n", + if (argc == 0) { // for all zones + log_info("estimated memory consumption for all zones is %zu MB", (size_t)total_size); } - return rc; + return 0; } diff --git a/src/knot/ctl/process.c b/src/knot/ctl/process.c index fb9e5339c49973b7dda8482e70192334be5652ea..9b68196d4b8c09f0acf5417a5fd6f040f9a38d85 100644 --- a/src/knot/ctl/process.c +++ b/src/knot/ctl/process.c @@ -140,17 +140,17 @@ int proc_update_privileges(int uid, int gid) if ((uid_t)uid != getuid() || (gid_t)gid != getgid()) { if (setgroups(0, NULL) < 0) { log_warning("failed to drop supplementary groups for " - "UID '%d': %s\n", getuid(), strerror(errno)); + "UID '%d': %s", getuid(), strerror(errno)); } # ifdef HAVE_INITGROUPS struct passwd *pw; if ((pw = getpwuid(uid)) == NULL) { - log_warning("failed to get passwd entry for UID '%d': %s\n", + log_warning("failed to get passwd entry for UID '%d': %s", uid, strerror(errno)); } else { if (initgroups(pw->pw_name, gid) < 0) { log_warning("failed to set supplementary groups " - "for UID '%d': %s\n", uid, strerror(errno)); + "for UID '%d': %s", uid, strerror(errno)); } } # endif /* HAVE_INITGROUPS */ @@ -159,15 +159,15 @@ int proc_update_privileges(int uid, int gid) /* Watch uid/gid. */ if ((gid_t)gid != getgid()) { - log_info("changing GID to '%d'\n", gid); + log_info("changing GID to '%d'", gid); if (setregid(gid, gid) < 0) { - log_error("failed to change GID to '%d'\n", gid); + log_error("failed to change GID to '%d'", gid); } } if ((uid_t)uid != getuid()) { - log_info("changing UID to '%d'\n", uid); + log_info("changing UID to '%d'", uid); if (setreuid(uid, uid) < 0) { - log_error("failed to change UID to '%d'\n", uid); + log_error("failed to change UID to '%d'", uid); } } @@ -184,7 +184,7 @@ int proc_update_privileges(int uid, int gid) assert(lfile != NULL); FILE* fp = fopen(lfile, "w"); if (fp == NULL) { - log_warning("storage directory '%s' is not writable\n", + log_warning("storage directory '%s' is not writable", zone->storage); ret = KNOT_EACCES; } else { @@ -210,18 +210,18 @@ char *pid_check_and_create() /* Check PID for existence and liveness. */ if (pid > 0 && pid_running(pid)) { - log_error("server PID found, already running\n"); + log_error("server PID found, already running"); free(pidfile); return NULL; } else if (stat(pidfile, &st) == 0) { - log_warning("removing stale PID file '%s'\n", pidfile); + log_warning("removing stale PID file '%s'", pidfile); pid_remove(pidfile); } /* Create a PID file. */ int ret = pid_write(pidfile); if (ret != KNOT_EOK) { - log_error("couldn't create a PID file '%s'\n", pidfile); + log_error("couldn't create a PID file '%s'", pidfile); free(pidfile); return NULL; } diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c index a63d791e7868ae16578775b329962e3cf1810929..210fdd4b9318e68ffac9cfa43b47776df00864ce 100644 --- a/src/knot/ctl/remote.c +++ b/src/knot/ctl/remote.c @@ -411,7 +411,7 @@ int remote_bind(conf_iface_t *desc) char addr_str[SOCKADDR_STRLEN] = {0}; sockaddr_tostr(&desc->addr, addr_str, sizeof(addr_str)); - log_info("binding remote control interface to '%s'\n", addr_str); + log_info("binding remote control interface to '%s'", addr_str); /* Create new socket. */ mode_t old_umask = umask(KNOT_CTL_SOCKET_UMASK); @@ -424,7 +424,7 @@ int remote_bind(conf_iface_t *desc) /* Start listening. */ int ret = listen(sock, TCP_BACKLOG_SIZE); if (ret < 0) { - log_error("could not bind to '%s'\n", addr_str); + log_error("could not bind to '%s'", addr_str); close(sock); return ret; } @@ -563,7 +563,7 @@ static void log_command(const char *cmd, const remote_cmdargs_t* args) } } - log_info("remote command: '%s%s'\n", cmd, params); + log_info("remote command: '%s%s'", cmd, params); } int remote_answer(int sock, server_t *s, knot_pkt_t *pkt) @@ -657,7 +657,7 @@ static int zones_verify_tsig_query(const knot_pkt_t *query, assert(tsig_rcode != NULL); if (query->tsig_rr == NULL) { - log_info("TSIG: key required, query REFUSED\n"); + log_info("TSIG: key required, query REFUSED"); *rcode = KNOT_RCODE_REFUSED; return KNOT_TSIG_EBADKEY; } @@ -667,7 +667,7 @@ static int zones_verify_tsig_query(const knot_pkt_t *query, */ knot_tsig_algorithm_t alg = tsig_rdata_alg(query->tsig_rr); if (knot_tsig_digest_length(alg) == 0) { - log_info("TSIG: unsupported algorithm, query NOTAUTH\n"); + log_info("TSIG: unsupported algorithm, query NOTAUTH"); /*! \todo [TSIG] It is unclear from RFC if I * should treat is as a bad key * or some other error. @@ -710,7 +710,7 @@ static int zones_verify_tsig_query(const knot_pkt_t *query, if (mac_len > digest_max_size) { *rcode = KNOT_RCODE_FORMERR; - log_info("TSIG: MAC length %zu exceeds maximum size %zu\n", + log_info("TSIG: MAC length %zu exceeds maximum size %zu", mac_len, digest_max_size); return KNOT_EMALF; } else { @@ -790,7 +790,7 @@ int remote_process(server_t *s, conf_iface_t *ctl_if, int sock, uint64_t ts_tmsigned = 0; if (match == NULL) { log_warning("denied remote control for '%s', " - "no matching ACL\n", addr_str); + "no matching ACL", addr_str); remote_senderr(client, pkt->wire, pkt->size); ret = KNOT_EACCES; goto finish; @@ -802,7 +802,7 @@ int remote_process(server_t *s, conf_iface_t *ctl_if, int sock, if (tsig_key) { if (pkt->tsig_rr == NULL) { log_warning("denied remote control for '%s', " - "key required\n", addr_str); + "key required", addr_str); remote_senderr(client, pkt->wire, pkt->size); ret = KNOT_EACCES; goto finish; @@ -811,7 +811,7 @@ int remote_process(server_t *s, conf_iface_t *ctl_if, int sock, &ts_trc, &ts_tmsigned); if (ret != KNOT_EOK) { log_warning("denied remote control for '%s', " - "key verification failed\n", addr_str); + "key verification failed", addr_str); remote_senderr(client, pkt->wire, pkt->size); ret = KNOT_EACCES; goto finish; diff --git a/src/knot/dnssec/nsec3-chain.c b/src/knot/dnssec/nsec3-chain.c index e95caf0551dc00e9f0bcee077d32c51d11ea10af..c486a1508c32a89901c7e175baaed21241c6ca77 100644 --- a/src/knot/dnssec/nsec3-chain.c +++ b/src/knot/dnssec/nsec3-chain.c @@ -121,7 +121,7 @@ static int shallow_copy_signature(const zone_node_t *from, zone_node_t *to) if (knot_rrset_empty(&from_sig)) { return KNOT_EOK; } - return node_add_rrset(to, &from_sig); + return node_add_rrset(to, &from_sig, NULL); } /*! @@ -179,7 +179,7 @@ static void free_nsec3_tree(zone_tree_t *nodes) knot_rdataset_t *rrsig = node_rdataset(node, KNOT_RRTYPE_RRSIG); knot_rdataset_clear(nsec3, NULL); knot_rdataset_clear(rrsig, NULL); - node_free(&node); + node_free(&node, NULL); } hattrie_iter_free(it); @@ -283,7 +283,7 @@ static zone_node_t *create_nsec3_node(knot_dname_t *owner, assert(apex_node); assert(rr_types); - zone_node_t *new_node = node_new(owner); + zone_node_t *new_node = node_new(owner, NULL); if (!new_node) { return NULL; } @@ -294,14 +294,14 @@ static zone_node_t *create_nsec3_node(knot_dname_t *owner, int ret = create_nsec3_rrset(&nsec3_rrset, owner, nsec3_params, rr_types, NULL, ttl); if (ret != KNOT_EOK) { - node_free(&new_node); + node_free(&new_node, NULL); return NULL; } - ret = node_add_rrset(new_node, &nsec3_rrset); + ret = node_add_rrset(new_node, &nsec3_rrset, NULL); knot_rrset_clear(&nsec3_rrset, NULL); if (ret != KNOT_EOK) { - node_free(&new_node); + node_free(&new_node, NULL); return NULL; } diff --git a/src/knot/dnssec/zone-events.c b/src/knot/dnssec/zone-events.c index 64337501efef4a2706d627ef247c5bb8fb17bb90..051b4b57dfbe08a1c1a737c8710b602dcf3f07b1 100644 --- a/src/knot/dnssec/zone-events.c +++ b/src/knot/dnssec/zone-events.c @@ -44,7 +44,7 @@ static int init_dnssec_structs(const zone_contents_t *zone, zone->apex->owner, nsec3_enabled, zone_keys); if (result != KNOT_EOK) { - log_zone_error(zone->apex->owner, "DNSSEC: failed to load keys: %s\n", + log_zone_error(zone->apex->owner, "DNSSEC, failed to load keys: %s", knot_strerror(result)); knot_free_zone_keys(zone_keys); return result; @@ -72,7 +72,7 @@ static int zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, const knot_dname_t *zone_name = zone->apex->owner; - log_zone_info(zone_name, "DNSSEC: signing started\n"); + log_zone_info(zone_name, "DNSSEC, signing started"); uint32_t new_serial = zone_contents_next_serial(zone, zone_config->serial_policy); dbg_dnssec_verb("changeset empty before generating NSEC chain: %d\n", @@ -92,7 +92,7 @@ static int zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, result = knot_zone_create_nsec_chain(zone, out_ch, &zone_keys, &policy); if (result != KNOT_EOK) { - log_zone_error(zone_name, "DNSSEC: could not create NSEC(3) chain: %s\n", + log_zone_error(zone_name, "DNSSEC, could not create NSEC(3) chain: %s", knot_strerror(result)); knot_free_zone_keys(&zone_keys); return result; @@ -104,7 +104,7 @@ static int zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, result = knot_zone_sign(zone, &zone_keys, &policy, out_ch, refresh_at); if (result != KNOT_EOK) { - log_zone_error(zone_name, "DNSSEC: error while signing: %s\n", + log_zone_error(zone_name, "DNSSEC, error while signing: %s", knot_strerror(result)); knot_free_zone_keys(&zone_keys); return result; @@ -115,7 +115,7 @@ static int zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, // Check if only SOA changed if (changeset_empty(out_ch) && !knot_zone_sign_soa_expired(zone, &zone_keys, &policy)) { - log_zone_info(zone_name, "DNSSEC: no signing performed, zone is valid\n"); + log_zone_info(zone_name, "DNSSEC, no signing performed, zone is valid"); knot_free_zone_keys(&zone_keys); assert(changeset_empty(out_ch)); return KNOT_EOK; @@ -128,8 +128,8 @@ static int zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, result = knot_zone_sign_update_soa(&soa, &rrsigs, &zone_keys, &policy, new_serial, out_ch); if (result != KNOT_EOK) { - log_zone_error(zone_name, "DNSSEC: not signing, cannot update " - "SOA record: %s\n", knot_strerror(result)); + log_zone_error(zone_name, "DNSSEC, not signing, cannot update " + "SOA record: %s", knot_strerror(result)); knot_free_zone_keys(&zone_keys); return result; } @@ -138,7 +138,7 @@ static int zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, dbg_dnssec_detail("zone signed: changes=%zu\n", changeset_size(out_ch)); - log_zone_info(zone_name, "DNSSEC: successfully signed\n"); + log_zone_info(zone_name, "DNSSEC, successfully signed"); return KNOT_EOK; } @@ -195,7 +195,7 @@ int knot_dnssec_sign_changeset(const zone_contents_t *zone, ret = knot_zone_sign_changeset(zone, in_ch, out_ch, &zone_keys, &policy); if (ret != KNOT_EOK) { - log_zone_error(zone_name, "DNSSEC: failed to sign changeset: %s\n", + log_zone_error(zone_name, "DNSSEC, failed to sign changeset: %s", knot_strerror(ret)); knot_free_zone_keys(&zone_keys); return ret; @@ -204,7 +204,7 @@ int knot_dnssec_sign_changeset(const zone_contents_t *zone, // Create NSEC(3) chain ret = knot_zone_create_nsec_chain(zone, out_ch, &zone_keys, &policy); if (ret != KNOT_EOK) { - log_zone_error(zone_name, "DNSSEC: failed to create NSEC(3) chain: %s\n", + log_zone_error(zone_name, "DNSSEC, failed to create NSEC(3) chain: %s", knot_strerror(ret)); knot_free_zone_keys(&zone_keys); return ret; @@ -214,7 +214,7 @@ int knot_dnssec_sign_changeset(const zone_contents_t *zone, ret = knot_zone_sign_nsecs_in_changeset(&zone_keys, &policy, out_ch); if (ret != KNOT_EOK) { - log_zone_error(zone_name, "DNSSEC: failed to sign changeset: %s\n", + log_zone_error(zone_name, "DNSSEC, failed to sign changeset: %s", knot_strerror(ret)); knot_free_zone_keys(&zone_keys); return ret; @@ -226,7 +226,7 @@ int knot_dnssec_sign_changeset(const zone_contents_t *zone, ret = knot_zone_sign_update_soa(&soa, &rrsigs, &zone_keys, &policy, new_serial, out_ch); if (ret != KNOT_EOK) { - log_zone_error(zone_name, "DNSSEC: failed to sign SOA RR: %s\n", + log_zone_error(zone_name, "DNSSEC, failed to sign SOA RR: %s", knot_strerror(ret)); knot_free_zone_keys(&zone_keys); return ret; diff --git a/src/knot/dnssec/zone-keys.c b/src/knot/dnssec/zone-keys.c index 97d798b36f898459fdb892adf8070efba5decebe..6988c495c3d2d14b934446e04d2f1c438ab3e1ac 100644 --- a/src/knot/dnssec/zone-keys.c +++ b/src/knot/dnssec/zone-keys.c @@ -106,6 +106,65 @@ static void set_zone_key_flags(const knot_key_params_t *params, (params->time_delete == 0 || now < params->time_delete); } +/*! + * \brief Check if there is a functional KSK and ZSK for each used algorithm. + */ +static int check_keys_validity(const knot_zone_keys_t *keys) +{ + assert(keys); + + const int MAX_ALGORITHMS = KNOT_DNSSEC_ALG_ECDSAP384SHA384 + 1; + struct { + bool published; + bool ksk_enabled; + bool zsk_enabled; + } algorithms[MAX_ALGORITHMS]; + memset(algorithms, 0, sizeof(algorithms)); + + /* Make a list of used algorithms */ + + const knot_zone_key_t *key = NULL; + WALK_LIST(key, keys->list) { + knot_dnssec_algorithm_t a = key->dnssec_key.algorithm; + assert(a < MAX_ALGORITHMS); + + if (key->is_public) { + // public key creates a requirement for an algorithm + algorithms[a].published = true; + + // need fully enabled ZSK and KSK for each algorithm + if (key->is_active) { + if (key->is_ksk) { + algorithms[a].ksk_enabled = true; + } else { + algorithms[a].zsk_enabled = true; + } + } + } + } + + /* Validate enabled algorithms */ + + int enabled_count = 0; + for (int a = 0; a < MAX_ALGORITHMS; a++) { + if (!algorithms[a].published) { + continue; + } + + if (!algorithms[a].ksk_enabled || !algorithms[a].zsk_enabled) { + return KNOT_DNSSEC_EMISSINGKEYTYPE; + } + + enabled_count += 1; + } + + if (enabled_count == 0) { + return KNOT_DNSSEC_ENOKEY; + } + + return KNOT_EOK; +} + /*! * \brief Load zone keys from a key directory. * @@ -158,7 +217,7 @@ int knot_load_zone_keys(const char *keydir_name, const knot_dname_t *zone_name, free(path); if (ret != KNOT_EOK) { - log_zone_warning(zone_name, "DNSSEC: failed to load key '%s': %s\n", + log_zone_warning(zone_name, "DNSSEC, failed to load key '%s': %s", entry->d_name, knot_strerror(ret)); knot_free_key_params(¶ms); continue; @@ -189,8 +248,8 @@ int knot_load_zone_keys(const char *keydir_name, const knot_dname_t *zone_name, if (!knot_dnssec_algorithm_is_zonesign(params.algorithm, nsec3_enabled) ) { - log_zone_notice(zone_name, "DNSSEC: ignoring key %d (%s): " - "incompatible algorithm\n", + log_zone_notice(zone_name, "DNSSEC, ignoring key %d (%s): " + "incompatible algorithm", params.keytag, entry->d_name); knot_free_key_params(¶ms); free(key); @@ -198,8 +257,8 @@ int knot_load_zone_keys(const char *keydir_name, const knot_dname_t *zone_name, } if (knot_get_zone_key(keys, params.keytag) != NULL) { - log_zone_notice(zone_name, "DNSSEC: ignoring key %d (%s): " - "duplicate keytag\n", + log_zone_notice(zone_name, "DNSSEC, ignoring key %d (%s): " + "duplicate keytag", params.keytag, entry->d_name); knot_free_key_params(¶ms); free(key); @@ -208,8 +267,8 @@ int knot_load_zone_keys(const char *keydir_name, const knot_dname_t *zone_name, ret = knot_dnssec_key_from_params(¶ms, &key->dnssec_key); if (ret != KNOT_EOK) { - log_zone_error(zone_name, "DNSSEC: failed to process " - "key %d (%s): %s\n", + log_zone_error(zone_name, "DNSSEC, failed to process " + "key %d (%s): %s", params.keytag, entry->d_name, knot_strerror(ret)); knot_free_key_params(¶ms); @@ -217,7 +276,7 @@ int knot_load_zone_keys(const char *keydir_name, const knot_dname_t *zone_name, continue; } - log_zone_info(zone_name, "DNSSEC: loaded key %5d, file %s, %s, %s, %s\n", + log_zone_info(zone_name, "DNSSEC, loaded key %5d, file %s, %s, %s, %s", params.keytag, entry->d_name, key->is_ksk ? "KSK" : "ZSK", key->is_active ? "active" : "inactive", @@ -230,8 +289,8 @@ int knot_load_zone_keys(const char *keydir_name, const knot_dname_t *zone_name, closedir(keydir); - if (result == KNOT_EOK && EMPTY_LIST(keys->list)) { - result = KNOT_DNSSEC_ENOKEY; + if (result == KNOT_EOK) { + result = check_keys_validity(keys); } if (result == KNOT_EOK) { diff --git a/src/knot/main.c b/src/knot/main.c index 4b5210fd83b6fb46960df74ead18b485019fd9b7..fa0d4b1e5015622461c3559170d57c74f3fa9491 100644 --- a/src/knot/main.c +++ b/src/knot/main.c @@ -62,7 +62,7 @@ static void knot_crypto_deinit(void) static void pid_cleanup(char *pidfile) { if (pidfile && pid_remove(pidfile) < 0) { - log_warning("failed to remove PID file\n"); + log_warning("failed to remove PID file"); } } @@ -114,12 +114,12 @@ static void setup_capabilities(void) /* Apply. */ if (capng_apply(CAPNG_SELECT_BOTH) < 0) { - log_error("couldn't set process capabilities: %s\n", + log_error("couldn't set process capabilities: %s", strerror(errno)); } } else { log_info("user UID %d is not allowed to set capabilities, " - "skipping\n", getuid()); + "skipping", getuid()); } #endif /* HAVE_CAP_NG_H */ } @@ -267,7 +267,7 @@ int main(int argc, char **argv) int res = conf_open(config_fn); conf_t *config = conf(); if (res != KNOT_EOK) { - log_fatal("couldn't load configuration '%s': %s\n", + log_fatal("couldn't load configuration '%s': %s", config_fn, knot_strerror(res)); return EXIT_FAILURE; } @@ -281,7 +281,7 @@ int main(int argc, char **argv) server_t server; res = server_init(&server, conf_bg_threads(config)); if (res != KNOT_EOK) { - log_fatal("could not initialize server: %s\n", knot_strerror(res)); + log_fatal("could not initialize server: %s", knot_strerror(res)); conf_free(conf()); log_close(); return EXIT_FAILURE; @@ -291,7 +291,7 @@ int main(int argc, char **argv) * @note This MUST be done before we drop privileges. */ server_reconfigure(config, &server); conf_add_hook(config, CONF_ALL, server_reconfigure, &server); - log_info("configured %zu interfaces and %zu zones\n", + log_info("configured %zu interfaces and %zu zones", list_size(&config->ifaces), hattrie_weight(config->zones)); @@ -316,12 +316,12 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - log_info("PID stored in '%s'\n", pidfile); + log_info("PID stored in '%s'", pidfile); if (chdir(daemon_root) != 0) { - log_warning("can't change working directory to %s\n", + log_warning("can't change working directory to %s", daemon_root); } else { - log_info("changed directory to %s\n", daemon_root); + log_info("changed directory to %s", daemon_root); } } @@ -336,20 +336,20 @@ int main(int argc, char **argv) rcu_register_thread(); /* Populate zone database and add reconfiguration hook. */ - log_info("loading zones\n"); + log_info("loading zones"); server_update_zones(config, &server); conf_add_hook(config, CONF_ALL, server_update_zones, &server); /* Check number of loaded zones. */ if (knot_zonedb_size(server.zone_db) == 0) { - log_warning("no zones loaded\n"); + log_warning("no zones loaded"); } /* Start it up. */ - log_info("starting server\n"); + log_info("starting server"); res = server_start(&server, config->async_start); if (res != KNOT_EOK) { - log_fatal("failed to start server: %s\n", knot_strerror(res)); + log_fatal("failed to start server: %s", knot_strerror(res)); server_deinit(&server); rcu_unregister_thread(); pid_cleanup(pidfile); @@ -359,9 +359,9 @@ int main(int argc, char **argv) } if (daemonize) { - log_info("server started as a daemon, PID %ld\n", pid); + log_info("server started as a daemon, PID %ld", pid); } else { - log_info("server started in foreground, PID %ld\n", pid); + log_info("server started in foreground, PID %ld", pid); init_signal_started(); } @@ -381,7 +381,7 @@ int main(int argc, char **argv) /* Cleanup PID file. */ pid_cleanup(pidfile); - log_info("shutting down\n"); + log_info("shutting down"); log_close(); return EXIT_SUCCESS; diff --git a/src/knot/modules/dnstap.c b/src/knot/modules/dnstap.c index 928fdc3797859664baaf276ae3d1890567dea406..327eedafdc1109251603665438c6cf31c8d03c24 100644 --- a/src/knot/modules/dnstap.c +++ b/src/knot/modules/dnstap.c @@ -204,7 +204,7 @@ int dnstap_load(struct query_plan *plan, struct query_module *self) return KNOT_EOK; fail: - MODULE_ERR("init(\"%s\") failed - %s\n", self->param, knot_strerror(ret)); + MODULE_ERR("init(\"%s\") failed - %s", self->param, knot_strerror(ret)); return ret; } diff --git a/src/knot/modules/synth_record.c b/src/knot/modules/synth_record.c index f5b7561f001f2134e600532ac561079dde62815e..5f455435d3b523b65d507bbefe04fd04c51581e5 100644 --- a/src/knot/modules/synth_record.c +++ b/src/knot/modules/synth_record.c @@ -347,14 +347,14 @@ int synth_record_load(struct query_plan *plan, struct query_module *self) } else if (strcmp(token, "forward") == 0) { tpl->type = SYNTH_FORWARD; } else { - MODULE_ERR("invalid type '%s'\n", token); + MODULE_ERR("invalid type '%s'", token); return KNOT_ENOTSUP; } /* Parse format string. */ tpl->prefix = strtok_r(NULL, " ", &saveptr); if (strchr(tpl->prefix, '.') != NULL) { - MODULE_ERR("dots '.' are not allowed in the prefix\n"); + MODULE_ERR("dots '.' are not allowed in the prefix"); return KNOT_EMALF; } @@ -363,7 +363,7 @@ int synth_record_load(struct query_plan *plan, struct query_module *self) tpl->zone = strtok_r(NULL, " ", &saveptr); knot_dname_t *check_name = knot_dname_from_str(tpl->zone); if (check_name == NULL) { - MODULE_ERR("invalid zone '%s'\n", tpl->zone); + MODULE_ERR("invalid zone '%s'", tpl->zone); return KNOT_EMALF; } knot_dname_free(&check_name, NULL); @@ -390,13 +390,13 @@ int synth_record_load(struct query_plan *plan, struct query_module *self) /* Check subnet. */ if (tpl->subnet.prefix > prefix_max) { - MODULE_ERR("invalid address prefix '%s'\n", subnet); + MODULE_ERR("invalid address prefix '%s'", subnet); return KNOT_EMALF; } int ret = sockaddr_set(&tpl->subnet.addr, family, token, 0); if (ret != KNOT_EOK) { - MODULE_ERR("invalid address '%s'\n", token); + MODULE_ERR("invalid address '%s'", token); return KNOT_EMALF; } diff --git a/src/knot/nameserver/internet.c b/src/knot/nameserver/internet.c index 93cd27ba54ae31fc91e6a9d6fb74c53b2f80dc9e..6fb89e324904017bc62d6a4b7ab4801a59d2e82d 100644 --- a/src/knot/nameserver/internet.c +++ b/src/knot/nameserver/internet.c @@ -19,7 +19,6 @@ #include "libknot/common.h" #include "libknot/rrtype/rdname.h" #include "libknot/rrtype/soa.h" -#include "libknot/rrtype/opt.h" #include "libknot/dnssec/rrset-sign.h" #include "knot/nameserver/internet.h" #include "knot/nameserver/nsec_proofs.h" @@ -167,50 +166,6 @@ static int put_rrsig(const knot_dname_t *sig_owner, uint16_t type, return KNOT_EOK; } -/*! \brief Put OPT RR to packet. */ -static int put_opt_rr(knot_pkt_t *pkt, struct query_data *qdata) -{ - /* Copy OPT RR from server. */ - dbg_ns("%s: adding OPT RR to the response\n", __func__); - const knot_pkt_t *query = qdata->query; - knot_rrset_t opt_rr; - int ret = knot_edns_init(&opt_rr, conf()->max_udp_payload, - qdata->rcode_ext, KNOT_EDNS_VERSION, &pkt->mm); - if (ret != KNOT_EOK) { - return ret; - } - - /* Append NSID if requested and available. */ - if (knot_edns_has_nsid(query->opt_rr) && conf()->nsid_len > 0) { - ret = knot_edns_add_option(&opt_rr, - KNOT_EDNS_OPTION_NSID, conf()->nsid_len, - (const uint8_t*)conf()->nsid, &pkt->mm); - if (ret != KNOT_EOK) { - knot_rrset_clear(&opt_rr, &pkt->mm); - return ret; - } - } - - /* Set DO bit if set (DNSSEC requested). */ - if (knot_pkt_has_dnssec(query)) { - dbg_ns("%s: setting DO=1 in OPT RR\n", __func__); - knot_edns_set_do(&opt_rr); - } - - /* Reclaim reserved size. */ - ret = knot_pkt_reclaim(pkt, knot_edns_wire_size(&opt_rr)); - if (ret == KNOT_EOK) { - /* Write to packet. */ - ret = knot_pkt_put(pkt, COMPR_HINT_NONE, &opt_rr, KNOT_PF_FREE); - } - - if (ret != KNOT_EOK) { - knot_rrset_clear(&opt_rr, &pkt->mm); - } - - return ret; -} - /*! \brief This is a wildcard-covered or any other terminal node for QNAME. * e.g. positive answer. */ @@ -700,14 +655,7 @@ static int solve_authority_dnssec(int state, knot_pkt_t *pkt, struct query_data static int solve_additional(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx) { - /* Put OPT RR to the additional section. */ int ret = KNOT_EOK; - if (knot_pkt_has_edns(qdata->query)) { - ret = put_opt_rr(pkt, qdata); - if (ret != KNOT_EOK) { - return ERROR; - } - } /* Scan all RRs in ANSWER/AUTHORITY. */ for (uint16_t i = 0; i < pkt->rrset_count; ++i) { diff --git a/src/knot/nameserver/process_query.c b/src/knot/nameserver/process_query.c index 39a3aad86dc02aaf7eb3ab1d0df0c22e9dba139f..8f7f9d9938a775eedc8a598fc3c79d64f9502318 100644 --- a/src/knot/nameserver/process_query.c +++ b/src/knot/nameserver/process_query.c @@ -60,6 +60,7 @@ static int process_query_reset(knot_process_t *ctx) knot_pkt_free(&qdata->query); ptrlist_free(&qdata->wildcards, qdata->mm); nsec_clear_rrsigs(qdata); + knot_rrset_clear(&qdata->opt_rr, qdata->mm); if (qdata->ext_cleanup != NULL) { qdata->ext_cleanup(qdata); } @@ -260,6 +261,94 @@ static const zone_t *answer_zone_find(const knot_pkt_t *query, knot_zonedb_t *zo return zone; } +static int answer_edns_reserve(knot_pkt_t *resp, struct query_data *qdata) +{ + if (knot_rrset_empty(&qdata->opt_rr)) { + return KNOT_EOK; + } + + /* Reserve size in the response. */ + return knot_pkt_reserve(resp, knot_edns_wire_size(&qdata->opt_rr)); +} + +static int answer_edns_init(const knot_pkt_t *query, knot_pkt_t *resp, + struct query_data *qdata) +{ + if (!knot_pkt_has_edns(query)) { + return KNOT_EOK; + } + + /* Initialize OPT record. */ + int ret = knot_edns_init(&qdata->opt_rr, conf()->max_udp_payload, 0, + KNOT_EDNS_VERSION, qdata->mm); + if (ret != KNOT_EOK) { + return ret; + } + + /* Check supported version. */ + if (knot_edns_get_version(query->opt_rr) != KNOT_EDNS_VERSION) { + qdata->rcode_ext = KNOT_EDNS_RCODE_BADVERS; + } + + /* Set DO bit if set (DNSSEC requested). */ + if (knot_pkt_has_dnssec(query)) { + knot_edns_set_do(&qdata->opt_rr); + } + + /* Append NSID if requested and available. */ + if (knot_edns_has_nsid(query->opt_rr) && conf()->nsid_len > 0) { + ret = knot_edns_add_option(&qdata->opt_rr, + KNOT_EDNS_OPTION_NSID, conf()->nsid_len, + (const uint8_t*)conf()->nsid, qdata->mm); + if (ret != KNOT_EOK) { + return ret; + } + } + + return answer_edns_reserve(resp, qdata); +} + +static int answer_edns_put(knot_pkt_t *resp, struct query_data *qdata) +{ + if (knot_rrset_empty(&qdata->opt_rr)) { + return KNOT_EOK; + } + + /* Write back extended RCODE. */ + if (qdata->rcode_ext != 0) { + knot_wire_set_rcode(resp->wire, KNOT_EDNS_RCODE_LO(qdata->rcode_ext)); + knot_edns_set_ext_rcode(&qdata->opt_rr, KNOT_EDNS_RCODE_HI(qdata->rcode_ext)); + } + + /* Reclaim reserved size. */ + int ret = knot_pkt_reclaim(resp, knot_edns_wire_size(&qdata->opt_rr)); + if (ret != KNOT_EOK) { + return ret; + } + + /* Write to packet. */ + assert(resp->current == KNOT_ADDITIONAL); + return knot_pkt_put(resp, COMPR_HINT_NONE, &qdata->opt_rr, 0); +} + +/*! \brief Convert QNAME to lowercase format for processing. */ +static int qname_case_lower(knot_pkt_t *pkt) +{ + knot_dname_t *qname = (knot_dname_t *)knot_pkt_qname(pkt); + return knot_dname_to_lower(qname); +} + +/*! \brief Restore QNAME letter case. */ +static void qname_case_restore(struct query_data *qdata, knot_pkt_t *pkt) +{ + /* If original QNAME is empty, Query is either unparsed or for root domain. + * Either way, letter case doesn't matter. */ + if (qdata->orig_qname[0] != '\0') { + memcpy(pkt->wire + KNOT_WIRE_HEADER_SIZE, + qdata->orig_qname, qdata->query->qname_size); + } +} + /*! \brief Initialize response, sizes and find zone from which we're going to answer. */ static int prepare_answer(const knot_pkt_t *query, knot_pkt_t *resp, knot_process_t *ctx) { @@ -285,58 +374,34 @@ static int prepare_answer(const knot_pkt_t *query, knot_pkt_t *resp, knot_proces * Already checked for absence of compression and length. */ memcpy(qdata->orig_qname, qname, query->qname_size); - ret = knot_dname_to_lower((knot_dname_t *)qname); + ret = qname_case_lower((knot_pkt_t *)query); if (ret != KNOT_EOK) { dbg_ns("%s: can't convert QNAME to lowercase (%d)\n", __func__, ret); return ret; } - /* Find zone for QNAME. */ qdata->zone = answer_zone_find(query, server->zone_db); - /* Update maximal answer size. */ - if (qdata->param->proc_flags & NS_QUERY_LIMIT_SIZE) { - resp->max_size = KNOT_WIRE_MIN_PKTSIZE; - } - - /* Check if EDNS is supported. */ - if (!knot_pkt_has_edns(query)) { - return KNOT_EOK; - } - - /* Check EDNS version and return BADVERS if not supported. */ - if (knot_edns_get_version(query->opt_rr) != KNOT_EDNS_VERSION) { - dbg_ns("%s: unsupported EDNS version required.\n", __func__); - qdata->rcode_ext = KNOT_EDNS_RCODE_BADVERS; - } - - /* Reserve space for OPT RR in the packet. Using size of the server's - * OPT RR, because that's the maximum size (RDATA may or may not be - * used). - */ - uint16_t reserve_size = KNOT_EDNS_MIN_SIZE; - if (knot_edns_has_nsid(query->opt_rr) && conf()->nsid_len > 0) { - reserve_size += KNOT_EDNS_OPTION_HDRLEN + conf()->nsid_len; - } - ret = knot_pkt_reserve(resp, reserve_size); + /* Setup EDNS. */ + ret = answer_edns_init(query, resp, qdata); if (ret != KNOT_EOK) { - dbg_ns("%s: can't reserve OPT RR in response (%d)\n", __func__, ret); return ret; } - /* Get minimal supported size from EDNS(0). */ - uint16_t client_maxlen = knot_edns_get_payload(query->opt_rr); - uint16_t server_maxlen = conf()->max_udp_payload; - uint16_t min_edns = MIN(client_maxlen, server_maxlen); - - /* Update packet size limit. */ - if (qdata->param->proc_flags & NS_QUERY_LIMIT_SIZE) { - resp->max_size = MAX(resp->max_size, min_edns); - dbg_ns("%s: packet size limit <= %zuB\n", __func__, resp->max_size); + /* Update maximal answer size. */ + bool has_limit = qdata->param->proc_flags & NS_QUERY_LIMIT_SIZE; + if (has_limit) { + resp->max_size = KNOT_WIRE_MIN_PKTSIZE; + if (knot_pkt_has_edns(query)) { + uint16_t client = knot_edns_get_payload(query->opt_rr); + uint16_t server = conf()->max_udp_payload; + uint16_t transfer = MIN(client, server); + resp->max_size = MAX(resp->max_size, transfer); + } + } else { + resp->max_size = KNOT_WIRE_MAX_PKTSIZE; } - /* In the response, always advertise server's maximum UDP payload. */ - return ret; } @@ -351,21 +416,26 @@ static int process_query_err(knot_pkt_t *pkt, knot_process_t *ctx) knot_pkt_t *query = qdata->query; knot_pkt_init_response(pkt, query); - /* If original QNAME is empty, Query is either unparsed or for root domain. - * Either way, letter case doesn't matter. */ - if (qdata->orig_qname[0] != '\0') { - memcpy(pkt->wire + KNOT_WIRE_HEADER_SIZE, - qdata->orig_qname, query->qname_size); - } + /* Restore original QNAME. */ + qname_case_restore(qdata, pkt); /* Set RCODE. */ knot_wire_set_rcode(pkt->wire, qdata->rcode); - /* Transaction security (if applicable). */ - if (process_query_sign_response(pkt, qdata) != KNOT_EOK) { - return NS_PROC_FAIL; + /* Add OPT and TSIG (best effort, send reply anyway if fails). */ + if (pkt->current != KNOT_ADDITIONAL) { + knot_pkt_begin(pkt, KNOT_ADDITIONAL); } + /* Put OPT RR to the additional section. */ + int ret = answer_edns_reserve(pkt, qdata); + if (ret == KNOT_EOK) { + (void) answer_edns_put(pkt, qdata); + } + + /* Transaction security (if applicable). */ + (void) process_query_sign_response(pkt, qdata); + return NS_PROC_DONE; } @@ -464,8 +534,24 @@ static int process_query_out(knot_pkt_t *pkt, knot_process_t *ctx) * Postprocessing. */ - /* Transaction security (if applicable). */ + if (next_state == NS_PROC_DONE || next_state == NS_PROC_FULL) { + + /* Restore original QNAME. */ + qname_case_restore(qdata, pkt); + + if (pkt->current != KNOT_ADDITIONAL) { + knot_pkt_begin(pkt, KNOT_ADDITIONAL); + } + + /* Put OPT RR to the additional section. */ + ret = answer_edns_put(pkt, qdata); + if (ret != KNOT_EOK) { + next_state = NS_PROC_FAIL; + goto finish; + } + + /* Transaction security (if applicable). */ if (process_query_sign_response(pkt, qdata) != KNOT_EOK) { next_state = NS_PROC_FAIL; } @@ -541,8 +627,10 @@ int process_query_verify(struct query_data *qdata) ctx->tsig_digestlen = tsig_rdata_mac_length(query->tsig_rr); /* Checking query. */ + qname_case_restore(qdata, query); int ret = knot_tsig_server_check(query->tsig_rr, query->wire, query->size, ctx->tsig_key); + qname_case_lower(query); dbg_ns("%s: QUERY TSIG check result = %s\n", __func__, knot_strerror(ret)); diff --git a/src/knot/nameserver/process_query.h b/src/knot/nameserver/process_query.h index c506314a288d2c3d11891eb1c510f8f804dae9b0..27ca16847d9cb754465af16c3500ae346a15fc6e 100644 --- a/src/knot/nameserver/process_query.h +++ b/src/knot/nameserver/process_query.h @@ -39,7 +39,7 @@ const knot_process_module_t *process_query_get_module(void); #define NS_PROC_LOG(severity, remote, zone_name, operation, msg, ...) do { \ char addr_str[SOCKADDR_STRLEN] = {0}; \ sockaddr_tostr(remote, addr_str, sizeof(addr_str)); \ - log_msg_zone(severity, zone_name, operation ", %s: " msg "\n", \ + log_msg_zone(severity, zone_name, operation ", %s: " msg, \ addr_str, ##__VA_ARGS__); \ } while (0) @@ -84,6 +84,9 @@ struct query_data { /* Original QNAME case. */ uint8_t orig_qname[KNOT_DNAME_MAXLEN]; + /* EDNS */ + knot_rrset_t opt_rr; + /* Extensions. */ void *ext; void (*ext_cleanup)(struct query_data*); /*!< Extensions cleanup callback. */ diff --git a/src/knot/nameserver/update.c b/src/knot/nameserver/update.c index cd4d2a1951efe5814ba372d163a091060c09a3b1..418ac7c6bd6363b2de6ac12a36587fda6d744913 100644 --- a/src/knot/nameserver/update.c +++ b/src/knot/nameserver/update.c @@ -1,3 +1,19 @@ +/* Copyright (C) 2013 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 "knot/nameserver/update.h" #include "knot/nameserver/internet.h" #include "knot/nameserver/process_query.h" @@ -6,6 +22,7 @@ #include "common/debug.h" #include "knot/dnssec/zone-events.h" #include "knot/updates/ddns.h" +#include "knot/updates/zone-update.h" #include "libknot/descriptor.h" #include "libknot/tsig-op.h" #include "knot/zone/zone.h" @@ -18,32 +35,7 @@ /* UPDATE-specific logging (internal, expects 'qdata' variable set). */ #define UPDATE_LOG(severity, msg...) \ - QUERY_LOG(severity, qdata, "UPDATE", msg) - -int update_query_process(knot_pkt_t *pkt, struct query_data *qdata) -{ - /* RFC1996 require SOA question. */ - NS_NEED_QTYPE(qdata, KNOT_RRTYPE_SOA, KNOT_RCODE_FORMERR); - - /* Check valid zone. */ - NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH); - - /* Need valid transaction security. */ - zone_t *zone = (zone_t *)qdata->zone; - NS_NEED_AUTH(&zone->conf->acl.update_in, qdata); - /* Check expiration. */ - NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); - - /* Store update into DDNS queue. */ - int ret = zone_update_enqueue(zone, qdata->query, qdata->param); - if (ret != KNOT_EOK) { - return NS_PROC_FAIL; - } - - /* No immediate response. */ - pkt->size = 0; - return NS_PROC_DONE; -} + QUERY_LOG(severity, qdata, "DDNS", msg) static bool apex_rr_changed(const zone_contents_t *old_contents, const zone_contents_t *new_contents, @@ -55,19 +47,6 @@ static bool apex_rr_changed(const zone_contents_t *old_contents, return !knot_rrset_equal(&old_rr, &new_rr, KNOT_RRSET_COMPARE_WHOLE); } -static bool zones_dnskey_changed(const zone_contents_t *old_contents, - const zone_contents_t *new_contents) -{ - return apex_rr_changed(old_contents, new_contents, KNOT_RRTYPE_DNSKEY); -} - -static bool zones_nsec3param_changed(const zone_contents_t *old_contents, - const zone_contents_t *new_contents) -{ - return apex_rr_changed(old_contents, new_contents, - KNOT_RRTYPE_NSEC3PARAM); -} - static int sign_update(zone_t *zone, const zone_contents_t *old_contents, zone_contents_t *new_contents, changeset_t *ddns_ch, changeset_t *sec_ch) @@ -83,8 +62,8 @@ static int sign_update(zone_t *zone, const zone_contents_t *old_contents, */ int ret = KNOT_EOK; uint32_t refresh_at = 0; - if (zones_dnskey_changed(old_contents, new_contents) || - zones_nsec3param_changed(old_contents, new_contents)) { + if (apex_rr_changed(old_contents, new_contents, KNOT_RRTYPE_DNSKEY) || + apex_rr_changed(old_contents, new_contents, KNOT_RRTYPE_NSEC3PARAM)) { ret = knot_dnssec_zone_sign(new_contents, zone->conf, sec_ch, KNOT_SOA_SERIAL_KEEP, &refresh_at); @@ -120,61 +99,130 @@ static int sign_update(zone_t *zone, const zone_contents_t *old_contents, return KNOT_EOK; } -static int process_authenticated(uint16_t *rcode, struct query_data *qdata) +static int check_prereqs(struct knot_request_data *request, + const zone_t *zone, zone_update_t *update, + struct query_data *qdata) { - assert(rcode); - assert(qdata); - - const knot_pkt_t *query = qdata->query; - zone_t *zone = (zone_t *)qdata->zone; + uint16_t rcode = KNOT_RCODE_NOERROR; + int ret = ddns_process_prereqs(request->query, update, &rcode); + if (ret != KNOT_EOK) { + UPDATE_LOG(LOG_WARNING, "prerequisites not met - %s\n", + knot_strerror(ret)); + assert(rcode != KNOT_RCODE_NOERROR); + knot_wire_set_rcode(request->resp->wire, rcode); + return ret; + } + + return KNOT_EOK; +} - int ret = ddns_process_prereqs(query, zone->contents, rcode); +static int process_single_update(struct knot_request_data *request, + const zone_t *zone, zone_update_t *update, + struct query_data *qdata) +{ + uint16_t rcode = KNOT_RCODE_NOERROR; + int ret = ddns_process_update(zone, request->query, update, &rcode); if (ret != KNOT_EOK) { - assert(*rcode != KNOT_RCODE_NOERROR); + UPDATE_LOG(LOG_WARNING, "failed to apply - %s\n", + knot_strerror(ret)); + assert(rcode != KNOT_RCODE_NOERROR); + knot_wire_set_rcode(request->resp->wire, rcode); return ret; } - // Create DDNS change + return KNOT_EOK; +} + +#undef UPDATE_LOG + +static void set_rcodes(list_t *requests, const uint16_t rcode) +{ + struct knot_request_data *req; + WALK_LIST(req, *requests) { + if (knot_wire_get_rcode(req->resp->wire) == KNOT_RCODE_NOERROR) { + knot_wire_set_rcode(req->resp->wire, rcode); + } + } +} + +static int process_bulk(zone_t *zone, list_t *requests, changeset_t *ddns_ch) +{ + // Init zone update structure. + zone_update_t zone_update; + zone_update_init(&zone_update, zone->contents, ddns_ch); + + // Walk all the requests and process. + struct knot_request_data *req; + WALK_LIST(req, *requests) { + // Init qdata structure for logging (unique per-request). + struct process_query_param param = { 0 }; + param.remote = &req->remote; + struct query_data qdata = { 0 }; + qdata.param = ¶m; + qdata.query = req->query; + qdata.zone = zone; + + int ret = check_prereqs(req, zone, &zone_update, &qdata); + if (ret != KNOT_EOK) { + // Skip updates with failed prereqs. + continue; + } + + ret = process_single_update(req, zone, &zone_update, &qdata); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +static int process_normal(zone_t *zone, list_t *requests) +{ + assert(requests); + + // Create DDNS change. changeset_t ddns_ch; - ret = changeset_init(&ddns_ch, qdata->zone->name); + int ret = changeset_init(&ddns_ch, zone->name); if (ret != KNOT_EOK) { - *rcode = KNOT_RCODE_SERVFAIL; + set_rcodes(requests, KNOT_RCODE_SERVFAIL); return ret; } - - ret = ddns_process_update(zone, query, &ddns_ch, rcode); + + // Process all updates. + ret = process_bulk(zone, requests, &ddns_ch); if (ret != KNOT_EOK) { - assert(*rcode != KNOT_RCODE_NOERROR); changeset_clear(&ddns_ch); + set_rcodes(requests, KNOT_RCODE_SERVFAIL); return ret; } - assert(*rcode == KNOT_RCODE_NOERROR); - + zone_contents_t *new_contents = NULL; const bool change_made = !changeset_empty(&ddns_ch); - if (change_made) { - ret = apply_changeset(zone, &ddns_ch, &new_contents); - if (ret != KNOT_EOK) { - if (ret == KNOT_ETTL) { - *rcode = KNOT_RCODE_REFUSED; - } else { - *rcode = KNOT_RCODE_SERVFAIL; - } - changeset_clear(&ddns_ch); - return ret; - } - } else { + if (!change_made) { changeset_clear(&ddns_ch); - *rcode = KNOT_RCODE_NOERROR; return KNOT_EOK; } + + // Apply changes. + ret = apply_changeset(zone, &ddns_ch, &new_contents); + if (ret != KNOT_EOK) { + if (ret == KNOT_ETTL) { + set_rcodes(requests, KNOT_RCODE_REFUSED); + } else { + set_rcodes(requests, KNOT_RCODE_SERVFAIL); + } + changeset_clear(&ddns_ch); + return ret; + } assert(new_contents); + // Sign the update. changeset_t sec_ch; if (zone->conf->dnssec_enable) { ret = changeset_init(&sec_ch, zone->name); if (ret != KNOT_EOK) { - *rcode = KNOT_RCODE_SERVFAIL; + set_rcodes(requests, KNOT_RCODE_SERVFAIL); return ret; } ret = sign_update(zone, zone->contents, new_contents, &ddns_ch, @@ -183,8 +231,7 @@ static int process_authenticated(uint16_t *rcode, struct query_data *qdata) update_rollback(&ddns_ch); update_free_zone(&new_contents); changeset_clear(&ddns_ch); - changeset_clear(&sec_ch); - *rcode = KNOT_RCODE_SERVFAIL; + set_rcodes(requests, KNOT_RCODE_SERVFAIL); return ret; } } @@ -198,7 +245,7 @@ static int process_authenticated(uint16_t *rcode, struct query_data *qdata) if (zone->conf->dnssec_enable) { changeset_clear(&sec_ch); } - *rcode = KNOT_RCODE_SERVFAIL; + set_rcodes(requests, KNOT_RCODE_SERVFAIL); return ret; } @@ -223,66 +270,61 @@ static int process_authenticated(uint16_t *rcode, struct query_data *qdata) zone_events_schedule(zone, ZONE_EVENT_FLUSH, ZONE_EVENT_NOW); } - *rcode = KNOT_RCODE_NOERROR; return ret; } -static int execute_query(knot_pkt_t *pkt, struct query_data *qdata) +static int process_requests(zone_t *zone, list_t *requests) { - if (pkt == NULL || qdata == NULL) { + if (zone == NULL || requests == NULL) { return KNOT_EINVAL; } - UPDATE_LOG(LOG_INFO, "Started."); - /* Keep original state. */ struct timeval t_start, t_end; gettimeofday(&t_start, NULL); - zone_t *zone = (zone_t *)qdata->zone; const uint32_t old_serial = zone_contents_serial(zone->contents); /* Process authenticated packet. */ - uint16_t rcode = KNOT_RCODE_NOERROR; - int ret = process_authenticated(&rcode, qdata); + int ret = process_normal(zone, requests); if (ret != KNOT_EOK) { - assert(rcode != KNOT_RCODE_NOERROR); - UPDATE_LOG(LOG_WARNING, "%s", knot_strerror(ret)); - knot_wire_set_rcode(pkt->wire, rcode); + log_zone_error(zone->name, "DDNS, processing failed (%s)", + knot_strerror(ret)); return ret; } /* Evaluate response. */ const uint32_t new_serial = zone_contents_serial(zone->contents); if (new_serial == old_serial) { - assert(rcode == KNOT_RCODE_NOERROR); - UPDATE_LOG(LOG_NOTICE, "No change to zone made."); + log_zone_info(zone->name, "DDNS, no change to zone made"); return KNOT_EOK; } gettimeofday(&t_end, NULL); - UPDATE_LOG(LOG_INFO, "Serial %u -> %u", old_serial, new_serial); - UPDATE_LOG(LOG_INFO, "Finished in %.02fs.", - time_diff(&t_start, &t_end) / 1000.0); - + log_zone_info(zone->name, "DDNS, serial %u -> %u", old_serial, new_serial); + log_zone_info(zone->name, "DDNS, update finished in %.02f seconds", + time_diff(&t_start, &t_end) / 1000.0); + zone_events_schedule(zone, ZONE_EVENT_NOTIFY, ZONE_EVENT_NOW); return KNOT_EOK; } -static int forward_query(knot_pkt_t *pkt, struct query_data *qdata) +static int forward_request(zone_t *zone, struct knot_request_data *request) { /* Create requestor instance. */ struct knot_requestor re; - knot_requestor_init(&re, NS_PROC_CAPTURE, qdata->mm); + knot_requestor_init(&re, NS_PROC_CAPTURE, NULL); /* Fetch primary master. */ - const conf_iface_t *master = zone_master(qdata->zone); + const conf_iface_t *master = zone_master(zone); /* Copy request and assign new ID. */ - knot_pkt_t *query = knot_pkt_new(NULL, pkt->max_size, qdata->mm); - int ret = knot_pkt_copy(query, qdata->query); + knot_pkt_t *query = knot_pkt_new(NULL, request->query->max_size, NULL); + int ret = knot_pkt_copy(query, request->query); if (ret != KNOT_EOK) { + knot_pkt_free(&query); + knot_wire_set_rcode(request->resp->wire, KNOT_RCODE_SERVFAIL); return ret; } knot_wire_set_id(query->wire, knot_random_uint16_t()); @@ -299,8 +341,9 @@ static int forward_query(knot_pkt_t *pkt, struct query_data *qdata) /* Enqueue and execute request. */ struct process_capture_param param; - param.sink = pkt; + param.sink = request->resp; ret = knot_requestor_enqueue(&re, req, ¶m); + if (ret == KNOT_EOK) { struct timeval tv = { conf()->max_conn_reply, 0 }; ret = knot_requestor_exec(&re, &tv); @@ -309,59 +352,126 @@ static int forward_query(knot_pkt_t *pkt, struct query_data *qdata) knot_requestor_clear(&re); /* Restore message ID and TSIG. */ - knot_wire_set_id(pkt->wire, knot_wire_get_id(qdata->query->wire)); - knot_tsig_append(pkt->wire, &pkt->size, pkt->max_size, pkt->tsig_rr); + knot_wire_set_id(request->resp->wire, knot_wire_get_id(request->query->wire)); + knot_tsig_append(request->resp->wire, &request->resp->size, + request->resp->max_size, request->resp->tsig_rr); /* Set RCODE if forwarding failed. */ if (ret != KNOT_EOK) { - knot_wire_set_rcode(pkt->wire, KNOT_RCODE_SERVFAIL); - UPDATE_LOG(LOG_INFO, "Failed to forward UPDATE to master: %s", - knot_strerror(ret)); + knot_wire_set_rcode(request->resp->wire, KNOT_RCODE_SERVFAIL); + log_zone_error(zone->name, "DDNS, " + "failed to forward updates to master (%s)", + knot_strerror(ret)); } else { - UPDATE_LOG(LOG_INFO, "Forwarded UPDATE to master."); + log_zone_info(zone->name, "DDNS, updates forwarded"); } return ret; } -#undef UPDATE_LOG +static void forward_requests(zone_t *zone, list_t *requests) +{ + struct knot_request_data *req; + WALK_LIST(req, *requests) { + forward_request(zone, req); + } +} -int update_execute(zone_t *zone, struct knot_request_data *update) +static int init_update_respones(list_t *updates) { - knot_pkt_t *resp = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL); - if (resp == NULL) { - return KNOT_ENOMEM; + struct knot_request_data *r = NULL; + WALK_LIST(r, *updates) { + r->resp = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL); + if (r->resp == NULL) { + return KNOT_ENOMEM; + } + + assert(r->query); + knot_pkt_init_response(r->resp, r->query); } - /* Initialize query response. */ - assert(update->query); - knot_pkt_init_response(resp, update->query); + return KNOT_EOK; +} - /* Create minimal query data context. */ - struct process_query_param param = { 0 }; - param.remote = &update->remote; - struct query_data qdata = { 0 }; - qdata.param = ¶m; - qdata.query = update->query; - qdata.zone = zone; +static void send_update_responses(list_t *updates) +{ + struct knot_request_data *r, *nxt; + WALK_LIST_DELSAFE(r, nxt, *updates) { + if (r->resp) { + if (net_is_connected(r->fd)) { + tcp_send_msg(r->fd, r->resp->wire, r->resp->size); + } else { + udp_send_msg(r->fd, r->resp->wire, r->resp->size, + (struct sockaddr *)&r->remote); + } + } - /* Process the update query. */ - int ret = KNOT_EOK; - if (zone_master(zone) != NULL) { - ret = forward_query(resp, &qdata); - } else { - ret = execute_query(resp, &qdata); + close(r->fd); + knot_pkt_free(&r->query); + knot_pkt_free(&r->resp); + free(r); } +} - /* Send response. */ - if (net_is_connected(update->fd)) { - tcp_send_msg(update->fd, resp->wire, resp->size); +int update_query_process(knot_pkt_t *pkt, struct query_data *qdata) +{ + /* RFC1996 require SOA question. */ + NS_NEED_QTYPE(qdata, KNOT_RRTYPE_SOA, KNOT_RCODE_FORMERR); + + /* Check valid zone. */ + NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH); + + /* Need valid transaction security. */ + zone_t *zone = (zone_t *)qdata->zone; + NS_NEED_AUTH(&zone->conf->acl.update_in, qdata); + /* Check expiration. */ + NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); + + /* Store update into DDNS queue. */ + int ret = zone_update_enqueue(zone, qdata->query, qdata->param); + if (ret != KNOT_EOK) { + return NS_PROC_FAIL; + } + + /* No immediate response. */ + pkt->size = 0; + return NS_PROC_DONE; +} + +int updates_execute(zone_t *zone) +{ + /* Get list of pending updates. */ + list_t updates; + size_t update_count; + zone_update_dequeue(zone, &updates, &update_count); + if (EMPTY_LIST(updates)) { + return KNOT_EOK; + } + + /* Init updates respones. */ + int ret = init_update_respones(&updates); + if (ret != KNOT_EOK) { + /* Send what responses we can. */ + set_rcodes(&updates, KNOT_RCODE_SERVFAIL); + send_update_responses(&updates); + return ret; + } + + /* Process update list - forward if zone has master, or execute. */ + if (zone_master(zone)) { + log_zone_info(zone->name, + "DDNS, forwarding %zu updates", update_count); + forward_requests(zone, &updates); } else { - udp_send_msg(update->fd, resp->wire, resp->size, - (struct sockaddr *)param.remote); + log_zone_info(zone->name, + "DDNS, processing %zu updates", update_count); + ret = process_requests(zone, &updates); } + UNUSED(ret); /* Don't care about the Knot code, RCODEs are set. */ - knot_pkt_free(&resp); + /* Send responses. */ + send_update_responses(&updates); - return ret; + return KNOT_EOK; } + diff --git a/src/knot/nameserver/update.h b/src/knot/nameserver/update.h index 35af62d8a746fd2a7725a514c2e21f6a39a5833b..674184864f5a26966c2153f5a4250b95a42a30ca 100644 --- a/src/knot/nameserver/update.h +++ b/src/knot/nameserver/update.h @@ -2,6 +2,7 @@ * \file update.h * * \author Marek Vavrusa <marek.vavrusa@nic.cz> + * \author Jan Kadlec <jan.kadlec@nic.cz> * * \brief DDNS UPDATE processing. * @@ -30,6 +31,7 @@ #include "knot/zone/zonedb.h" struct query_data; +struct zone; /*! * \brief UPDATE query processing module. @@ -42,11 +44,11 @@ int update_query_process(knot_pkt_t *pkt, struct query_data *qdata); * \brief Processes serialized packet with DDNS. Function expects that the * query is already authenticated and TSIG signature is verified. * - * \param zone Updated zone. - * \param update UPDATE request. + * \param pkt Prepared response packet. + * \param qdata Minimal query data context. * * \return KNOT_E* */ -int update_execute(zone_t *zone, struct knot_request_data *update); +int updates_execute(zone_t *zone); /*! @} */ diff --git a/src/knot/server/rrl.c b/src/knot/server/rrl.c index 4371b6f3f91e208d70d6eb3ff7da57f2a2962db9..8474ee89870015bbb29a6291a3ac08d0da86ae68 100644 --- a/src/knot/server/rrl.c +++ b/src/knot/server/rrl.c @@ -286,7 +286,7 @@ static void rrl_log_state(const struct sockaddr_storage *ss, uint16_t flags, uin what = "enters"; } - log_notice("address '%s' %s rate-limiting (class '%s')\n", + log_notice("address '%s' %s rate-limiting (class '%s')", addr_str, what, rrl_clsstr(cls)); #endif } diff --git a/src/knot/server/server.c b/src/knot/server/server.c index ea64ca9c32bfffbd6104945e93de9ebda0523dfd..2ed87c33fc978643fa997dd877ff5f55f3a2ef1b 100644 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -142,7 +142,7 @@ static int server_init_iface(iface_t *new_if, conf_iface_t *cfg_if) if (ret < 0) { close(new_if->fd[IO_UDP]); close(new_if->fd[IO_TCP]); - log_error("failed to listen on TCP interface '%s'\n", addr_str); + log_error("failed to listen on TCP interface '%s'", addr_str); return KNOT_ERROR; } @@ -150,7 +150,8 @@ static int server_init_iface(iface_t *new_if, conf_iface_t *cfg_if) if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { close(new_if->fd[IO_UDP]); close(new_if->fd[IO_TCP]); - log_error("failed to listen on '%s' in non-blocking mode\n", addr_str); + log_error("failed to listen on '%s' in non-blocking mode", + addr_str); return KNOT_ERROR; } @@ -166,7 +167,7 @@ static void remove_ifacelist(struct ref_t *p) iface_t *n = NULL, *m = NULL; WALK_LIST_DELSAFE(n, m, ifaces->u) { sockaddr_tostr(&n->addr, addr_str, sizeof(addr_str)); - log_info("removing interface '%s'\n", addr_str); + log_info("removing interface '%s'", addr_str); server_remove_iface(n); } WALK_LIST_DELSAFE(n, m, ifaces->l) { @@ -223,7 +224,7 @@ static int reconfigure_sockets(const struct conf_t *conf, server_t *s) rem_node((node_t *)m); } else { sockaddr_tostr(&cfg_if->addr, addr_str, sizeof(addr_str)); - log_info("binding to interface %s\n", addr_str); + log_info("binding to interface %s", addr_str); /* Create new interface. */ m = malloc(sizeof(iface_t)); @@ -439,17 +440,18 @@ int server_reload(server_t *server, const char *cf) return KNOT_EINVAL; } - log_info("reloading configuration\n"); + log_info("reloading configuration"); int cf_ret = conf_open(cf); switch (cf_ret) { case KNOT_EOK: - log_info("configuration reloaded\n"); + log_info("configuration reloaded"); break; case KNOT_ENOENT: - log_error("configuration file '%s' not found\n", conf()->filename); + log_error("configuration file '%s' not found", + conf()->filename); break; default: - log_error("configuration reload failed\n"); + log_error("configuration reload failed"); break; } @@ -459,7 +461,7 @@ int server_reload(server_t *server, const char *cf) void server_stop(server_t *server) { - log_info("stopping server\n"); + log_info("stopping server"); /* Send termination event. */ event_t *term_ev = evsched_event_create(&server->sched, NULL, NULL); @@ -491,7 +493,7 @@ static int reconfigure_threads(const struct conf_t *conf, server_t *server) ret = server_init_handler(server, IO_UDP, conf_udp_threads(conf), &udp_master, &udp_master_destruct); if (ret != KNOT_EOK) { - log_error("failed to create UDP threads: %s\n", + log_error("failed to create UDP threads: %s", knot_strerror(ret)); return ret; } @@ -501,7 +503,7 @@ static int reconfigure_threads(const struct conf_t *conf, server_t *server) ret = server_init_handler(server, IO_TCP, conf_tcp_threads(conf), &tcp_master, &tcp_master_destruct); if (ret != KNOT_EOK) { - log_error("failed to create TCP threads: %s\n", + log_error("failed to create TCP threads: %s", knot_strerror(ret)); return ret; } @@ -524,7 +526,7 @@ static int reconfigure_rate_limits(const struct conf_t *conf, server_t *server) if (!server->rrl && conf->rrl > 0) { server->rrl = rrl_create(conf->rrl_size); if (!server->rrl) { - log_error("couldn't initialize rate limiting table\n"); + log_error("couldn't initialize rate limiting table"); } else { rrl_setlocks(server->rrl, RRL_LOCK_GRANULARITY); } @@ -534,9 +536,9 @@ static int reconfigure_rate_limits(const struct conf_t *conf, server_t *server) /* We cannot free it, threads may use it. * Setting it to <1 will disable rate limiting. */ if (conf->rrl < 1) { - log_info("rate limiting disabled\n"); + log_info("rate limiting disabled"); } else { - log_info("rate limiting set to %u responses/sec\n", + log_info("rate limiting set to %u responses/sec", conf->rrl); } rrl_setrate(server->rrl, conf->rrl); @@ -557,25 +559,25 @@ int server_reconfigure(const struct conf_t *conf, void *data) /* First reconfiguration. */ if (!(server->state & ServerRunning)) { - log_info("Knot DNS %s starting\n", PACKAGE_VERSION); + log_info("Knot DNS %s starting", PACKAGE_VERSION); } /* Reconfigure rate limits. */ int ret = KNOT_EOK; if ((ret = reconfigure_rate_limits(conf, server)) < 0) { - log_error("failed to reconfigure rate limits\n"); + log_error("failed to reconfigure rate limits"); return ret; } /* Reconfigure server threads. */ if ((ret = reconfigure_threads(conf, server)) < 0) { - log_error("failed to reconfigure server threads\n"); + log_error("failed to reconfigure server threads"); return ret; } /* Update bound sockets. */ if ((ret = reconfigure_sockets(conf, server)) < 0) { - log_error("failed to reconfigure server sockets\n"); + log_error("failed to reconfigure server sockets"); return ret; } diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c index 30d3c9545bc5a80fca002ae7f41652a1c43d1e79..6836308d4a349826eeaafab368940a856046cfa1 100644 --- a/src/knot/server/tcp-handler.c +++ b/src/knot/server/tcp-handler.c @@ -84,7 +84,7 @@ static enum fdset_sweep_state tcp_sweep(fdset_t *set, int i, void *data) char addr_str[SOCKADDR_STRLEN] = {0}; sockaddr_tostr(&ss, addr_str, sizeof(addr_str)); - log_notice("connection '%s' was terminated due to inactivity\n", addr_str); + log_notice("connection '%s' was terminated due to inactivity", addr_str); close(fd); return FDSET_SWEEP; } @@ -124,7 +124,7 @@ static int tcp_handle(tcp_context_t *tcp, int fd, char addr_str[SOCKADDR_STRLEN] = {0}; sockaddr_tostr(&ss, addr_str, sizeof(addr_str)); log_warning("couldn't receive query from '%s' within " - "the time limit of %ds\n", + "the time limit of %ds", addr_str, conf()->max_conn_idle); rcu_read_unlock(); } @@ -160,6 +160,46 @@ static int tcp_handle(tcp_context_t *tcp, int fd, return ret; } +int tcp_accept(int fd) +{ + /* Accept incoming connection. */ + int incoming = accept(fd, 0, 0); + + /* Evaluate connection. */ + if (incoming < 0) { + int en = errno; + if (en != EINTR && en != EAGAIN) { + log_error("cannot accept connection (%d)", errno); + if (en == EMFILE || en == ENFILE || + en == ENOBUFS || en == ENOMEM) { + int throttle = tcp_throttle(); + log_error("throttling TCP connection pool for" + "%d seconds because of too many open " + "descriptors or lack of memory", + throttle); + sleep(throttle); + } + + } + } else { + dbg_net("tcp: accepted connection fd=%d\n", incoming); + /* Set recv() timeout. */ +#ifdef SO_RCVTIMEO + struct timeval tv; + rcu_read_lock(); + tv.tv_sec = conf()->max_conn_idle; + rcu_read_unlock(); + tv.tv_usec = 0; + if (setsockopt(incoming, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { + log_warning("couldn't set up TCP connection watchdog " + "timer for fd=%d", incoming); + } +#endif + } + + return incoming; +} + static int tcp_event_accept(tcp_context_t *tcp, unsigned i) { /* Accept client. */ @@ -248,46 +288,6 @@ static int tcp_wait_for_events(tcp_context_t *tcp) return nfds; } -int tcp_accept(int fd) -{ - /* Accept incoming connection. */ - int incoming = accept(fd, 0, 0); - - /* Evaluate connection. */ - if (incoming < 0) { - int en = errno; - if (en != EINTR && en != EAGAIN) { - log_error("cannot accept connection (%d)\n", errno); - if (en == EMFILE || en == ENFILE || - en == ENOBUFS || en == ENOMEM) { - int throttle = tcp_throttle(); - log_error("throttling TCP connection pool for" - "%d seconds because of too many open " - "descriptors or lack of memory\n", - throttle); - sleep(throttle); - } - - } - } else { - dbg_net("tcp: accepted connection fd=%d\n", incoming); - /* Set recv() timeout. */ -#ifdef SO_RCVTIMEO - struct timeval tv; - rcu_read_lock(); - tv.tv_sec = conf()->max_conn_idle; - rcu_read_unlock(); - tv.tv_usec = 0; - if (setsockopt(incoming, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { - log_warning("couldn't set up TCP connection watchdog " - "timer for fd=%d\n", incoming); - } -#endif - } - - return incoming; -} - int tcp_master(dthread_t *thread) { if (!thread || !thread->data) { diff --git a/src/knot/updates/apply.c b/src/knot/updates/apply.c index 5e8bd030d00af50d9fe4c7cbe82eae702e803ac8..b67507d2b3b5b35969bb9fb787e0851d6d31c243 100644 --- a/src/knot/updates/apply.c +++ b/src/knot/updates/apply.c @@ -24,6 +24,7 @@ #include "knot/zone/zonefile.h" #include "common/lists.h" #include "libknot/rrtype/soa.h" +#include "libknot/rrtype/rrsig.h" /* --------------------------- Update cleanup ------------------------------- */ @@ -63,6 +64,38 @@ static int free_additional(zone_node_t **node, void *data) return KNOT_EOK; } +/* ------------------------- Empty node cleanup ----------------------------- */ + +/*! \brief Clears wildcard child if set in parent node. */ +static void fix_wildcard_child(zone_node_t *node, const knot_dname_t *owner) +{ + if ((node->flags & NODE_FLAGS_WILDCARD_CHILD) + && knot_dname_is_wildcard(owner)) { + node->flags &= ~NODE_FLAGS_WILDCARD_CHILD; + } +} + +/*! \todo move this to new zone API - zone should do this automatically. */ +/*! \brief Deletes possibly empty node and all its empty parents recursively. */ +static void delete_empty_node(zone_tree_t *tree, zone_node_t *node) +{ + if (node->rrset_count == 0 && node->children == 0) { + zone_node_t *parent_node = node->parent; + if (parent_node) { + fix_wildcard_child(parent_node, node->owner); + parent_node->children--; + // Recurse using the parent node + delete_empty_node(tree, parent_node); + } + + // Delete node + zone_node_t *removed_node = NULL; + zone_tree_remove(tree, node->owner, &removed_node); + UNUSED(removed_node); + node_free(&node, NULL); + } +} + /* -------------------- Changeset application helpers ----------------------- */ /*! \brief Replaces rdataset of given type with a copy. */ @@ -148,9 +181,23 @@ static bool can_remove(const zone_node_t *node, const knot_rrset_t *rr) return false; } +/*! \todo part of the new zone API. */ +static bool rrset_is_nsec3rel(const knot_rrset_t *rr) +{ + if (rr == NULL) { + return false; + } + + /* Is NSEC3 or non-empty RRSIG covering NSEC3. */ + return ((rr->type == KNOT_RRTYPE_NSEC3) + || (rr->type == KNOT_RRTYPE_RRSIG + && knot_rrsig_type_covered(&rr->rrs, 0) + == KNOT_RRTYPE_NSEC3)); +} + /*! \brief Removes single RR from zone contents. */ -static int remove_rr(zone_node_t *node, const knot_rrset_t *rr, - changeset_t *chset) +static int remove_rr(zone_tree_t *tree, zone_node_t *node, + const knot_rrset_t *rr, changeset_t *chset) { knot_rrset_t removed_rrset = node_rrset(node, rr->type); knot_rdata_t *old_data = removed_rrset.rrs.data; @@ -184,6 +231,10 @@ static int remove_rr(zone_node_t *node, const knot_rrset_t *rr, } else { // RRSet is empty now, remove it from node, all data freed. node_remove_rdataset(node, rr->type); + // If node is empty now, delete it from zone tree. + if (node->rrset_count == 0) { + delete_empty_node(tree, node); + } } return KNOT_EOK; @@ -205,7 +256,9 @@ static int apply_remove(zone_contents_t *contents, changeset_t *chset) continue; } - int ret = remove_rr(node, &rr, chset); + zone_tree_t *tree = rrset_is_nsec3rel(&rr) ? + contents->nsec3_nodes : contents->nodes; + int ret = remove_rr(tree, node, &rr, chset); if (ret != KNOT_EOK) { changeset_iter_clear(&itt); return ret; @@ -240,7 +293,7 @@ static int add_rr(const zone_contents_t *zone, zone_node_t *node, } // Insert new RR to RRSet, data will be copied. - int ret = node_add_rrset(node, rr); + int ret = node_add_rrset(node, rr, NULL); if (ret == KNOT_EOK || ret == KNOT_ETTL) { // RR added, store for possible rollback. knot_rdataset_t *rrs = node_rdataset(node, rr->type); @@ -294,8 +347,8 @@ static int apply_add(zone_contents_t *contents, changeset_t *chset, /*! \brief Replace old SOA with a new one. */ static int apply_replace_soa(zone_contents_t *contents, changeset_t *chset) { - assert(chset->soa_from); - int ret = remove_rr(contents->apex, chset->soa_from, chset); + assert(chset->soa_from && chset->soa_to); + int ret = remove_rr(contents->nodes, contents->apex, chset->soa_from, chset); if (ret != KNOT_EOK) { return ret; } @@ -334,75 +387,6 @@ static int apply_single(zone_contents_t *contents, changeset_t *chset, return apply_replace_soa(contents, chset); } -/* ------------------------- Empty node cleanup ----------------------------- */ - -/*! \brief Mark empty nodes in updated tree. */ -static int mark_empty(zone_node_t **node_p, void *data) -{ - assert(node_p && *node_p); - zone_node_t *node = *node_p; - list_t *l = (list_t *)data; - assert(data); - if (node->rrset_count == 0 && node->children == 0 && - !(node->flags & NODE_FLAGS_EMPTY)) { - /*! - * Mark this node and all parent nodes that have 0 RRSets and - * no children for removal. - */ - if (ptrlist_add(l, node, NULL) == NULL) { - return KNOT_ENOMEM; - } - node->flags |= NODE_FLAGS_EMPTY; - if (node->parent) { - if ((node->parent->flags & NODE_FLAGS_WILDCARD_CHILD) - && knot_dname_is_wildcard(node->owner)) { - node->parent->flags &= ~NODE_FLAGS_WILDCARD_CHILD; - } - node->parent->children--; - // Recurse using the parent node - return mark_empty(&node->parent, data); - } - } - - return KNOT_EOK; -} - -static int remove_empty_tree_nodes(zone_tree_t *tree) -{ - list_t l; - init_list(&l); - // walk through the zone and select nodes to be removed - int ret = zone_tree_apply(tree, mark_empty, &l); - if (ret != KNOT_EOK) { - return ret; - } - - ptrnode_t *n = NULL; - node_t *nxt = NULL; - WALK_LIST_DELSAFE(n, nxt, l) { - zone_node_t *node = (zone_node_t *)n->d; - int ret = zone_tree_remove(tree, node->owner, &node); - if (ret != KNOT_EOK) { - return ret; - } - node_free(&node); - free(n); - } - - return KNOT_EOK; -} - -/*! \brief Removes node that were previously marked as empty. */ -static int remove_empty_nodes(zone_contents_t *z) -{ - int ret = remove_empty_tree_nodes(z->nodes); - if (ret != KNOT_EOK) { - return ret; - } - - return remove_empty_tree_nodes(z->nsec3_nodes); -} - /* --------------------- Zone copy and finalization ------------------------- */ /*! \brief Creates a shallow zone contents copy. */ @@ -440,21 +424,11 @@ static int finalize_updated_zone(zone_contents_t *contents_copy, return KNOT_EINVAL; } - int ret = remove_empty_nodes(contents_copy); - if (ret != KNOT_EOK) { - return ret; - } - if (set_nsec3_names) { - ret = zone_contents_adjust_full(contents_copy, NULL, NULL); + return zone_contents_adjust_full(contents_copy, NULL, NULL); } else { - ret = zone_contents_adjust_pointers(contents_copy); + return zone_contents_adjust_pointers(contents_copy); } - if (ret != KNOT_EOK) { - return ret; - } - - return KNOT_EOK; } /* ------------------------------- API -------------------------------------- */ diff --git a/src/knot/updates/changesets.h b/src/knot/updates/changesets.h index a0d00c037229af335330021bb806abab916088c7..2863353c257f2ef4bbfbc501550793caa4f7c385 100644 --- a/src/knot/updates/changesets.h +++ b/src/knot/updates/changesets.h @@ -30,8 +30,6 @@ #include "knot/zone/contents.h" #include "common/lists.h" -/*----------------------------------------------------------------------------*/ - /*! \brief One zone change, from 'soa_from' to 'soa_to'. */ typedef struct { node_t n; /*!< List node. */ @@ -39,7 +37,7 @@ typedef struct { knot_rrset_t *soa_to; /*!< Destination SOA. */ zone_contents_t *add; /*!< Change additions. */ zone_contents_t *remove; /*!< Change removals. */ - list_t old_data; /*!< Old data, to be freed after succesfull update. */ + list_t old_data; /*!< Old data, to be freed after successful update. */ list_t new_data; /*!< New data, to be freed after failed update. */ size_t size; /*!< Size of serialized changeset. */ uint8_t *data; /*!< Serialized changeset. */ @@ -52,8 +50,6 @@ typedef struct { uint16_t node_pos; /*!< Position in node. */ } changeset_iter_t; -/*----------------------------------------------------------------------------*/ - /*! * \brief Inits changeset structure. * diff --git a/src/knot/updates/ddns.c b/src/knot/updates/ddns.c index 0180ca5095b06037fbd5bb4a9c26b331bb2e912c..fc3f46d533db1495fa3434dc1d7e48f0f703f87a 100644 --- a/src/knot/updates/ddns.c +++ b/src/knot/updates/ddns.c @@ -20,6 +20,7 @@ #include "knot/updates/ddns.h" #include "knot/updates/changesets.h" +#include "knot/updates/zone-update.h" #include "libknot/packet/pkt.h" #include "libknot/common.h" #include "libknot/consts.h" @@ -62,12 +63,12 @@ static int add_rr_to_list(list_t *l, const knot_rrset_t *rr) } /*!< \brief Checks whether RRSet exists in the zone. */ -static int check_rrset_exists(const zone_contents_t *zone, - const knot_rrset_t *rrset, uint16_t *rcode) +static int check_rrset_exists(zone_update_t *update, const knot_rrset_t *rrset, + uint16_t *rcode) { assert(rrset->type != KNOT_RRTYPE_ANY); - const zone_node_t *node = zone_contents_find_node(zone, rrset->owner); + const zone_node_t *node = zone_update_get_node(update, rrset->owner); if (node == NULL || !node_rrtype_exists(node, rrset->type)) { *rcode = KNOT_RCODE_NXRRSET; return KNOT_EPREREQ; @@ -84,14 +85,14 @@ static int check_rrset_exists(const zone_contents_t *zone, } /*!< \brief Checks whether RRSets in the list exist in the zone. */ -static int check_stored_rrsets(list_t *l, const zone_contents_t *zone, +static int check_stored_rrsets(list_t *l, zone_update_t *update, uint16_t *rcode) { node_t *n; WALK_LIST(n, *l) { ptrnode_t *ptr_n = (ptrnode_t *)n; knot_rrset_t *rrset = (knot_rrset_t *)ptr_n->d; - int ret = check_rrset_exists(zone, rrset, rcode); + int ret = check_rrset_exists(update, rrset, rcode); if (ret != KNOT_EOK) { return ret; } @@ -101,10 +102,10 @@ static int check_stored_rrsets(list_t *l, const zone_contents_t *zone, } /*!< \brief Checks whether node of given owner, with given type exists. */ -static bool check_type(const zone_contents_t *zone, const knot_rrset_t *rrset) +static bool check_type(zone_update_t *update, const knot_rrset_t *rrset) { assert(rrset->type != KNOT_RRTYPE_ANY); - const zone_node_t *node = zone_contents_find_node(zone, rrset->owner); + const zone_node_t *node = zone_update_get_node(update, rrset->owner); if (node == NULL || !node_rrtype_exists(node, rrset->type)) { return false; } @@ -113,11 +114,11 @@ static bool check_type(const zone_contents_t *zone, const knot_rrset_t *rrset) } /*!< \brief Checks whether RR type exists in the zone. */ -static int check_type_exist(const zone_contents_t *zone, +static int check_type_exist(zone_update_t *update, const knot_rrset_t *rrset, uint16_t *rcode) { assert(rrset->rclass == KNOT_CLASS_ANY); - if (check_type(zone, rrset)) { + if (check_type(update, rrset)) { return KNOT_EOK; } else { *rcode = KNOT_RCODE_NXRRSET; @@ -126,11 +127,11 @@ static int check_type_exist(const zone_contents_t *zone, } /*!< \brief Checks whether RR type is not in the zone. */ -static int check_type_not_exist(const zone_contents_t *zone, +static int check_type_not_exist(zone_update_t *update, const knot_rrset_t *rrset, uint16_t *rcode) { assert(rrset->rclass == KNOT_CLASS_NONE); - if (check_type(zone, rrset)) { + if (check_type(update, rrset)) { *rcode = KNOT_RCODE_YXRRSET; return KNOT_EPREREQ; } else { @@ -139,10 +140,10 @@ static int check_type_not_exist(const zone_contents_t *zone, } /*!< \brief Checks whether DNAME is in the zone. */ -static int check_in_use(const zone_contents_t *zone, +static int check_in_use(zone_update_t *update, const knot_dname_t *dname, uint16_t *rcode) { - const zone_node_t *node = zone_contents_find_node(zone, dname); + const zone_node_t *node = zone_update_get_node(update, dname); if (node == NULL || node->rrset_count == 0) { *rcode = KNOT_RCODE_NXDOMAIN; return KNOT_EPREREQ; @@ -152,10 +153,10 @@ static int check_in_use(const zone_contents_t *zone, } /*!< \brief Checks whether DNAME is not in the zone. */ -static int check_not_in_use(const zone_contents_t *zone, +static int check_not_in_use(zone_update_t *update, const knot_dname_t *dname, uint16_t *rcode) { - const zone_node_t *node = zone_contents_find_node(zone, dname); + const zone_node_t *node = zone_update_get_node(update, dname); if (node == NULL || node->rrset_count == 0) { return KNOT_EOK; } else { @@ -180,7 +181,7 @@ static bool rrset_empty(const knot_rrset_t *rrset) /*!< \brief Checks prereq for given packet RR. */ static int process_prereq(const knot_rrset_t *rrset, uint16_t qclass, - const zone_contents_t *zone, uint16_t *rcode, + zone_update_t *update, uint16_t *rcode, list_t *rrset_list) { if (knot_rdata_ttl(knot_rdataset_at(&rrset->rrs, 0)) != 0) { @@ -188,7 +189,7 @@ static int process_prereq(const knot_rrset_t *rrset, uint16_t qclass, return KNOT_EMALF; } - if (!knot_dname_is_sub(rrset->owner, zone->apex->owner)) { + if (!knot_dname_is_sub(rrset->owner, update->zone->apex->owner)) { *rcode = KNOT_RCODE_NOTZONE; return KNOT_EOUTOFZONE; } @@ -199,9 +200,9 @@ static int process_prereq(const knot_rrset_t *rrset, uint16_t qclass, return KNOT_EMALF; } if (rrset->type == KNOT_RRTYPE_ANY) { - return check_in_use(zone, rrset->owner, rcode); + return check_in_use(update, rrset->owner, rcode); } else { - return check_type_exist(zone, rrset, rcode); + return check_type_exist(update, rrset, rcode); } } else if (rrset->rclass == KNOT_CLASS_NONE) { if (!rrset_empty(rrset)) { @@ -209,9 +210,9 @@ static int process_prereq(const knot_rrset_t *rrset, uint16_t qclass, return KNOT_EMALF; } if (rrset->type == KNOT_RRTYPE_ANY) { - return check_not_in_use(zone, rrset->owner, rcode); + return check_not_in_use(update, rrset->owner, rcode); } else { - return check_type_not_exist(zone, rrset, rcode); + return check_type_not_exist(update, rrset, rcode); } } else if (rrset->rclass == qclass) { // Store RRs for full check into list @@ -295,7 +296,7 @@ static void remove_header_from_changeset(zone_contents_t *z, const knot_rrset_t static void remove_owner_from_changeset(zone_contents_t *z, const knot_dname_t *owner) { zone_node_t *n = (zone_node_t *)zone_contents_find_node(z, owner); - node_free_rrsets(n); + node_free_rrsets(n, NULL); } /* --------------------- true/false helper functions ------------------------ */ @@ -333,8 +334,8 @@ static bool should_replace(const knot_rrset_t *rrset) } /*!< \brief Returns true if node will be empty after update application. */ -static bool node_empty(const zone_node_t *node, knot_dname_t *owner, - const changeset_t *changeset) +static bool node_will_be_empty(const zone_node_t *node, knot_dname_t *owner, + const changeset_t *changeset) { if (node == NULL && name_added(changeset, owner)) { // Node created in update. @@ -449,7 +450,7 @@ static bool singleton_replaced(changeset_t *changeset, // Replace singleton RR. knot_rdataset_clear(rrs, NULL); node_remove_rdataset(n, rr->type); - node_add_rrset(n, rr); + node_add_rrset(n, rr, NULL); return true; } @@ -527,7 +528,7 @@ static int process_add_cname(const zone_node_t *node, } return add_rr_to_chgset(rr, changeset, NULL); - } else if (!node_empty(node, rr->owner, changeset)) { + } else if (!node_will_be_empty(node, rr->owner, changeset)) { // Other occupied node => ignore. return KNOT_EOK; } else { @@ -545,7 +546,7 @@ static int process_add_nsec3param(const zone_node_t *node, // Ignore non-apex additions char *owner = knot_dname_to_str(rr->owner); log_warning("refusing to add NSEC3PARAM owned by '%s' to " - "non-apex node\n", owner); + "non-apex node", owner); free(owner); return KNOT_EDENIED; } @@ -556,7 +557,7 @@ static int process_add_nsec3param(const zone_node_t *node, char *owner = knot_dname_to_str(rr->owner); log_warning("refusing to add NSEC3PARAM owned by '%s', NSEC3PARAM " - "already present, remove it first\n", owner); + "already present, remove it first", owner); free(owner); return KNOT_EOK; @@ -571,7 +572,7 @@ static int process_add_soa(const zone_node_t *node, changeset_t *changeset) { if (node == NULL || !node_rrtype_exists(node, KNOT_RRTYPE_SOA)) { - // Adding SOA to non-apex node, ignore + // Adding SOA to non-apex node, ignore. return KNOT_EOK; } @@ -583,7 +584,7 @@ static int process_add_soa(const zone_node_t *node, } // Store SOA copy into changeset. - if (changeset->soa_to != NULL) { + if (changeset->soa_to) { // Discard previous SOA - "There can be only one!" knot_rrset_free(&changeset->soa_to, NULL); } @@ -816,7 +817,7 @@ static int check_update(const knot_rrset_t *rrset, const knot_pkt_t *query, if (knot_rrtype_is_ddns_forbidden(rrset->type)) { *rcode = KNOT_RCODE_REFUSED; - log_warning("refusing to update DNSSEC-related record\n"); + log_warning("refusing to update DNSSEC-related record"); return KNOT_EDENIED; } @@ -881,10 +882,10 @@ static uint16_t ret_to_rcode(int ret) /* ---------------------------------- API ----------------------------------- */ -int ddns_process_prereqs(const knot_pkt_t *query, const zone_contents_t *zone, +int ddns_process_prereqs(const knot_pkt_t *query, zone_update_t *update, uint16_t *rcode) { - if (query == NULL || rcode == NULL || zone == NULL) { + if (query == NULL || rcode == NULL || update == NULL) { return KNOT_EINVAL; } @@ -896,7 +897,7 @@ int ddns_process_prereqs(const knot_pkt_t *query, const zone_contents_t *zone, for (int i = 0; i < answer->count; ++i) { // Check what can be checked, store full RRs into list ret = process_prereq(&answer->rr[i], knot_pkt_qclass(query), - zone, rcode, &rrset_list); + update, rcode, &rrset_list); if (ret != KNOT_EOK) { rrset_list_clear(&rrset_list); return ret; @@ -904,41 +905,42 @@ int ddns_process_prereqs(const knot_pkt_t *query, const zone_contents_t *zone, } // Check stored RRSets - ret = check_stored_rrsets(&rrset_list, zone, rcode); + ret = check_stored_rrsets(&rrset_list, update, rcode); rrset_list_clear(&rrset_list); return ret; } int ddns_process_update(const zone_t *zone, const knot_pkt_t *query, - changeset_t *changeset, uint16_t *rcode) + zone_update_t *update, uint16_t *rcode) { - if (zone == NULL || query == NULL || changeset == NULL || rcode == NULL) { + if (zone == NULL || query == NULL || update == NULL || rcode == NULL) { if (rcode) { *rcode = ret_to_rcode(KNOT_EINVAL); } return KNOT_EINVAL; } + + changeset_t *changeset = update->change; - /* Copy base SOA RR. */ - knot_rrset_t *soa_begin = - node_create_rrset(zone->contents->apex, KNOT_RRTYPE_SOA); - if (soa_begin == NULL) { - *rcode = ret_to_rcode(KNOT_ENOMEM); - return KNOT_ENOMEM; + if (changeset->soa_from == NULL) { + // Copy base SOA RR. + changeset->soa_from = + node_create_rrset(zone->contents->apex, KNOT_RRTYPE_SOA); + if (changeset->soa_from == NULL) { + *rcode = ret_to_rcode(KNOT_ENOMEM); + return KNOT_ENOMEM; + } } - changeset->soa_from = soa_begin; int64_t sn_old = zone_contents_serial(zone->contents); - /* Process all RRs the Authority (Update) section. */ - + // Process all RRs in the authority section. int apex_ns_rem = 0; const knot_pktsection_t *authority = knot_pkt_section(query, KNOT_AUTHORITY); for (uint16_t i = 0; i < authority->count; ++i) { const knot_rrset_t *rr = &authority->rr[i]; - - /* Check if the entry is correct. */ + // Check if RR is correct. int ret = check_update(rr, query, rcode); if (ret != KNOT_EOK) { assert(*rcode != KNOT_RCODE_NOERROR); @@ -964,7 +966,7 @@ int ddns_process_update(const zone_t *zone, const knot_pkt_t *query, return KNOT_EOK; } - knot_rrset_t *soa_cpy = knot_rrset_copy(soa_begin, NULL); + knot_rrset_t *soa_cpy = knot_rrset_copy(changeset->soa_from, NULL); if (soa_cpy == NULL) { *rcode = ret_to_rcode(KNOT_ENOMEM); return KNOT_ENOMEM; diff --git a/src/knot/updates/ddns.h b/src/knot/updates/ddns.h index a2bddbbc84eda63deaead0b3d79f4e069dee141e..e684eaef8c8f10190965e0946d933f9c7bfedf22 100644 --- a/src/knot/updates/ddns.h +++ b/src/knot/updates/ddns.h @@ -28,6 +28,7 @@ #pragma once #include "knot/updates/changesets.h" +#include "knot/updates/zone-update.h" #include "knot/zone/zone.h" #include "libknot/packet/pkt.h" #include "libknot/dname.h" @@ -41,7 +42,7 @@ * * \return KNOT_E* */ -int ddns_process_prereqs(const knot_pkt_t *query, const zone_contents_t *zone, +int ddns_process_prereqs(const knot_pkt_t *query, zone_update_t *update, uint16_t *rcode); /*! @@ -56,6 +57,6 @@ int ddns_process_prereqs(const knot_pkt_t *query, const zone_contents_t *zone, * \return KNOT_E* */ int ddns_process_update(const zone_t *zone, const knot_pkt_t *query, - changeset_t *changeset, uint16_t *rcode); + zone_update_t *update, uint16_t *rcode); /*! @} */ diff --git a/src/knot/updates/zone-update.c b/src/knot/updates/zone-update.c new file mode 100644 index 0000000000000000000000000000000000000000..a2c00e4fb37b6d6e34e949577920276e15b7a137 --- /dev/null +++ b/src/knot/updates/zone-update.c @@ -0,0 +1,179 @@ +/* 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 "knot/updates/zone-update.h" +#include "common/lists.h" +#include "common/mempool.h" + +static int add_to_node(zone_node_t *node, const zone_node_t *add_node, + mm_ctx_t *mm) +{ + for (uint16_t i = 0; i < add_node->rrset_count; ++i) { + knot_rrset_t rr = node_rrset_at(add_node, i); + if (!knot_rrset_empty(&rr)) { + int ret = node_add_rrset(node, &rr, mm); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + return KNOT_EOK; +} + +static int rem_from_node(zone_node_t *node, const zone_node_t *rem_node, + mm_ctx_t *mm) +{ + for (uint16_t i = 0; i < rem_node->rrset_count; ++i) { + // Remove each found RR from 'node'. + knot_rrset_t rem_rrset = node_rrset_at(rem_node, i); + knot_rdataset_t *to_change = node_rdataset(node, rem_rrset.type); + if (to_change) { + // Remove data from synthesized node + int ret = knot_rdataset_subtract(to_change, + &rem_rrset.rrs, + mm); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + return KNOT_EOK; +} + +static int apply_changes_to_node(zone_node_t *synth_node, const zone_node_t *add_node, + const zone_node_t *rem_node, mm_ctx_t *mm) +{ + // Add changes to node + if (!node_empty(add_node)) { + int ret = add_to_node(synth_node, add_node, mm); + if (ret != KNOT_EOK) { + return ret; + } + } + + // Remove changes from node + if (!node_empty(rem_node)) { + int ret = rem_from_node(synth_node, rem_node, mm); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +static int deep_copy_node_data(zone_node_t *node_copy, const zone_node_t *node, + mm_ctx_t *mm) +{ + // Clear space for RRs + node_copy->rrs = NULL; + node_copy->rrset_count = 0; + + for (uint16_t i = 0; i < node->rrset_count; ++i) { + knot_rrset_t rr = node_rrset_at(node, i); + int ret = node_add_rrset(node_copy, &rr, mm); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +static zone_node_t *node_deep_copy(const zone_node_t *node, mm_ctx_t *mm) +{ + // Shallow copy old node + zone_node_t *synth_node = node_shallow_copy(node, mm); + if (synth_node == NULL) { + return NULL; + } + + // Deep copy data inside node copy. + int ret = deep_copy_node_data(synth_node, node, mm); + if (ret != KNOT_EOK) { + node_free(&synth_node, mm); + return NULL; + } + + return synth_node; +} + +/* ------------------------------- API -------------------------------------- */ + +void zone_update_init(zone_update_t *update, const zone_contents_t *zone, changeset_t *change) +{ + update->zone = zone; + update->change = change; + mm_ctx_mempool(&update->mm, 4096); +} + +const zone_node_t *zone_update_get_node(zone_update_t *update, const knot_dname_t *dname) +{ + if (update == NULL || dname == NULL) { + return NULL; + } + + const zone_node_t *old_node = + zone_contents_find_node(update->zone, dname); + const zone_node_t *add_node = + zone_contents_find_node(update->change->add, dname); + const zone_node_t *rem_node = + zone_contents_find_node(update->change->remove, dname); + + const bool have_change = !node_empty(add_node) || !node_empty(rem_node); + if (!have_change) { + // Nothing to apply + return old_node; + } + + if (!old_node) { + if (add_node && node_empty(rem_node)) { + // Just addition + return add_node; + } else { + // Addition and deletion + old_node = add_node; + add_node = NULL; + } + } + + // We have to apply changes to node. + zone_node_t *synth_node = node_deep_copy(old_node, &update->mm); + if (synth_node == NULL) { + return NULL; + } + + // Apply changes to node. + int ret = apply_changes_to_node(synth_node, add_node, rem_node, + &update->mm); + if (ret != KNOT_EOK) { + node_free_rrsets(synth_node, &update->mm); + node_free(&synth_node, &update->mm); + return NULL; + } + + return synth_node; +} + +void zone_update_clear(zone_update_t *update) +{ + if (update) { + mp_delete(update->mm.ctx); + memset(update, 0, sizeof(*update)); + } +} diff --git a/src/knot/updates/zone-update.h b/src/knot/updates/zone-update.h new file mode 100644 index 0000000000000000000000000000000000000000..ea5e1eac4b9cecdcc07f4fe719d60331b4d6bcb2 --- /dev/null +++ b/src/knot/updates/zone-update.h @@ -0,0 +1,70 @@ +/*! + * \file zone_update.h + * + * \author Jan Kadlec <jan.kadlec@nic.cz> + * + * \brief API for quering zone that is being updated. + * + * \addtogroup server + * @{ + */ +/* 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/updates/changesets.h" +#include "knot/zone/contents.h" +#include "libknot/mempattern.h" + +/*! \brief Structure for zone contents updating / querying \todo to be moved to new ZONE API */ +typedef struct { + const zone_contents_t *zone; /*!< Zone being updated. */ + changeset_t *change; /*!< Changes we want to apply. */ + mm_ctx_t mm; /*!< Memory context used for intermediate nodes. */ +} zone_update_t; + +/*! + * \brief Inits given zone update structure, new memory context is created. + * + * \param update Zone update structure to init. + * \param zone Init with this zone. + * \param change Init with this changeset. \todo will not be present in zone API + */ +void zone_update_init(zone_update_t *update, const zone_contents_t *zone, + changeset_t *change); + +/*! + * \brief Returns node that would be in the zone after updating it. + * + * \note Returned node is either zone original or synthesized, do *not* free + * or modify. + * + * \param update Zone update. + * \param dname Dname to search for. + * + * \return Node after zone update. + */ +const zone_node_t *zone_update_get_node(zone_update_t *update, + const knot_dname_t *dname); + +/*! + * \brief Clear data allocated by given zone update structure. + * + * \param update Zone update to clear. + */ +void zone_update_clear(zone_update_t *update); + diff --git a/src/knot/zone/contents.c b/src/knot/zone/contents.c index 5ee1ff1be779379777e6bb5108ee7a6593d249ff..e00012bb409f5fcb5f3f4a5fa90f94f014ac7bae 100644 --- a/src/knot/zone/contents.c +++ b/src/knot/zone/contents.c @@ -106,8 +106,8 @@ static int zone_contents_destroy_node_rrsets_from_tree( UNUSED(data); assert(tnode != NULL); if (*tnode != NULL) { - node_free_rrsets(*tnode); - node_free(tnode); + node_free_rrsets(*tnode, NULL); + node_free(tnode, NULL); } return KNOT_EOK; @@ -411,7 +411,7 @@ zone_contents_t *zone_contents_new(const knot_dname_t *apex_name) } memset(contents, 0, sizeof(zone_contents_t)); - contents->apex = node_new(apex_name); + contents->apex = node_new(apex_name, NULL); if (contents->apex == NULL) { goto cleanup; } @@ -502,7 +502,7 @@ static int zone_contents_add_node(zone_contents_t *zone, zone_node_t *node, /* Create a new node. */ dbg_zone_detail("Creating new node.\n"); - next_node = node_new(parent); + next_node = node_new(parent, NULL); if (next_node == NULL) { return KNOT_ENOMEM; } @@ -511,7 +511,7 @@ static int zone_contents_add_node(zone_contents_t *zone, zone_node_t *node, dbg_zone_detail("Inserting new node to zone tree.\n"); ret = zone_tree_insert(zone->nodes, next_node); if (ret != KNOT_EOK) { - node_free(&next_node); + node_free(&next_node, NULL); return ret; } @@ -614,19 +614,19 @@ static int insert_rr(zone_contents_t *z, zone_contents_get_node(z, rr->owner); if (*n == NULL) { // Create new, insert - *n = node_new(rr->owner); + *n = node_new(rr->owner, NULL); if (*n == NULL) { return KNOT_ENOMEM; } ret = nsec3 ? zone_contents_add_nsec3_node(z, *n) : zone_contents_add_node(z, *n, true); if (ret != KNOT_EOK) { - node_free(n); + node_free(n, NULL); } } } - return node_add_rrset(*n, rr); + return node_add_rrset(*n, rr, NULL); } static int recreate_normal_tree(const zone_contents_t *z, zone_contents_t *out) @@ -637,7 +637,7 @@ static int recreate_normal_tree(const zone_contents_t *z, zone_contents_t *out) } // Insert APEX first. - zone_node_t *apex_cpy = node_shallow_copy(z->apex); + zone_node_t *apex_cpy = node_shallow_copy(z->apex, NULL); if (apex_cpy == NULL) { return KNOT_ENOMEM; } @@ -645,7 +645,7 @@ static int recreate_normal_tree(const zone_contents_t *z, zone_contents_t *out) // Normal additions need apex ... so we need to insert directly. int ret = zone_tree_insert(out->nodes, apex_cpy); if (ret != KNOT_EOK) { - node_free(&apex_cpy); + node_free(&apex_cpy, NULL); return ret; } @@ -662,7 +662,7 @@ static int recreate_normal_tree(const zone_contents_t *z, zone_contents_t *out) hattrie_iter_next(itt); continue; } - zone_node_t *to_add = node_shallow_copy(to_cpy); + zone_node_t *to_add = node_shallow_copy(to_cpy, NULL); if (to_add == NULL) { hattrie_iter_free(itt); return KNOT_ENOMEM; @@ -670,7 +670,7 @@ static int recreate_normal_tree(const zone_contents_t *z, zone_contents_t *out) int ret = zone_contents_add_node(out, to_add, true); if (ret != KNOT_EOK) { - node_free(&to_add); + node_free(&to_add, NULL); hattrie_iter_free(itt); return ret; } @@ -696,7 +696,7 @@ static int recreate_nsec3_tree(const zone_contents_t *z, zone_contents_t *out) } while (!hattrie_iter_finished(itt)) { const zone_node_t *to_cpy = (zone_node_t *)*hattrie_iter_val(itt); - zone_node_t *to_add = node_shallow_copy(to_cpy); + zone_node_t *to_add = node_shallow_copy(to_cpy, NULL); if (to_add == NULL) { hattrie_iter_free(itt); return KNOT_ENOMEM; @@ -704,7 +704,7 @@ static int recreate_nsec3_tree(const zone_contents_t *z, zone_contents_t *out) int ret = zone_contents_add_nsec3_node(out, to_add); if (ret != KNOT_EOK) { hattrie_iter_free(itt); - node_free(&to_add); + node_free(&to_add, NULL); return ret; } hattrie_iter_next(itt); @@ -1083,7 +1083,7 @@ int zone_contents_adjust_pointers(zone_contents_t *contents) int ret = zone_contents_load_nsec3param(contents); if (ret != KNOT_EOK) { log_zone_error(contents->apex->owner, - "failed to load NSEC3 parameters: %s\n", + "failed to load NSEC3 parameters: %s", knot_strerror(ret)); return ret; } @@ -1120,7 +1120,7 @@ int zone_contents_adjust_full(zone_contents_t *zone, int result = zone_contents_load_nsec3param(zone); if (result != KNOT_EOK) { log_zone_error(zone->apex->owner, - "failed to load NSEC3 parameters: %s\n", + "failed to load NSEC3 parameters: %s", knot_strerror(result)); return result; } @@ -1366,7 +1366,7 @@ uint32_t zone_contents_next_serial(const zone_contents_t *zone, int policy) /* If the new serial is 'lower' or equal than the new one, warn the user.*/ if (knot_serial_compare(old_serial, new_serial) >= 0) { log_zone_warning(zone->apex->owner, "new serial will be lower " - "than the current one (%u -> %u)\n", + "than the current one (%u -> %u)", old_serial, new_serial); } @@ -1399,14 +1399,14 @@ zone_node_t *zone_contents_get_node_for_rr(zone_contents_t *zone, const knot_rrs if (node == NULL) { int ret = KNOT_EOK; - node = node_new(rrset->owner); + node = node_new(rrset->owner, NULL); if (!nsec3) { ret = zone_contents_add_node(zone, node, 1); } else { ret = zone_contents_add_nsec3_node(zone, node); } if (ret != KNOT_EOK) { - node_free(&node); + node_free(&node, NULL); return NULL; } diff --git a/src/knot/zone/events.c b/src/knot/zone/events.c index 65efc65771ae9120311322a8a11a1af933f69a1d..99abf89ba7584a5be2ca8c420b3a818d1dee6ac0 100644 --- a/src/knot/zone/events.c +++ b/src/knot/zone/events.c @@ -214,7 +214,7 @@ static void schedule_dnssec(zone_t *zone, time_t refresh_at) struct tm time_gm = { 0 }; localtime_r(&refresh_at, &time_gm); strftime(time_str, sizeof(time_str), KNOT_LOG_TIME_FORMAT, &time_gm); - log_zone_info(zone->name, "DNSSEC, next event on %s\n", time_str); + log_zone_info(zone->name, "DNSSEC, next signing on %s", time_str); // schedule @@ -259,10 +259,10 @@ static int event_reload(zone_t *zone) if (result != KNOT_EOK) { if (result == KNOT_ESPACE) { log_zone_error(zone->name, "journal size is too small " - "to fit the changes\n"); + "to fit the changes"); } else { log_zone_error(zone->name, "failed to store changes into " - "journal: %s\n", knot_strerror(result)); + "journal: %s", knot_strerror(result)); } goto fail; } @@ -300,7 +300,7 @@ static int event_reload(zone_t *zone) zone_events_schedule(zone, ZONE_EVENT_FLUSH, zone_config->dbsync_timeout); uint32_t current_serial = zone_contents_serial(zone->contents); - log_zone_info(zone->name, "loaded, serial %u -> %u\n", + log_zone_info(zone->name, "loaded, serial %u -> %u", old_serial, current_serial); return KNOT_EOK; @@ -397,33 +397,24 @@ static int event_update(zone_t *zone) { assert(zone); - struct knot_request_data *update = zone_update_dequeue(zone); - if (update == NULL) { - return KNOT_EOK; - } - - /* Forward if zone has master, or execute. */ - int ret = update_execute(zone, update); - UNUSED(ret); - - /* Cleanup. */ - close(update->fd); - knot_pkt_free(&update->query); - free(update); + /* Process update list - forward if zone has master, or execute. */ + int ret = updates_execute(zone); + UNUSED(ret); /* Don't care about the Knot code, RCODEs are set. */ /* Trim extra heap. */ mem_trim(); /* Replan event if next update waiting. */ - pthread_mutex_lock(&zone->ddns_lock); - - if (!EMPTY_LIST(zone->ddns_queue)) { + pthread_spin_lock(&zone->ddns_lock); + + const bool empty = EMPTY_LIST(zone->ddns_queue); + + pthread_spin_unlock(&zone->ddns_lock); + + if (!empty) { zone_events_schedule(zone, ZONE_EVENT_UPDATE, ZONE_EVENT_NOW); } - pthread_mutex_unlock(&zone->ddns_lock); - - return KNOT_EOK; } @@ -439,7 +430,7 @@ static int event_expire(zone_t *zone) zone->zonefile_serial = 0; zone_contents_deep_free(&expired); - log_zone_info(zone->name, "zone expired\n"); + log_zone_info(zone->name, "zone expired"); /* Trim extra heap. */ mem_trim(); @@ -506,13 +497,13 @@ static int event_dnssec(zone_t *zone) uint32_t refresh_at = time(NULL); if (zone->flags & ZONE_FORCE_RESIGN) { log_zone_info(zone->name, "DNSSEC, dropping previous " - "signatures, resigning zone\n"); + "signatures, resigning zone"); zone->flags &= ~ZONE_FORCE_RESIGN; ret = knot_dnssec_zone_sign_force(zone->contents, zone->conf, &ch, &refresh_at); } else { - log_zone_info(zone->name, "DNSSEC, signing zone\n"); + log_zone_info(zone->name, "DNSSEC, signing zone"); ret = knot_dnssec_zone_sign(zone->contents, zone->conf, &ch, KNOT_SOA_SERIAL_UPDATE, &refresh_at); @@ -526,7 +517,7 @@ static int event_dnssec(zone_t *zone) zone_contents_t *new_contents = NULL; int ret = apply_changeset(zone, &ch, &new_contents); if (ret != KNOT_EOK) { - log_zone_error(zone->name, "DNSSEC, could not sign zone (%s)\n", + log_zone_error(zone->name, "DNSSEC, could not sign zone (%s)", knot_strerror(ret)); goto done; } @@ -534,7 +525,7 @@ static int event_dnssec(zone_t *zone) /* Write change to journal. */ ret = zone_change_store(zone, &ch); if (ret != KNOT_EOK) { - log_zone_error(zone->name, "DNSSEC, could not sign zone (%s)\n", + log_zone_error(zone->name, "DNSSEC, could not sign zone (%s)", knot_strerror(ret)); update_rollback(&ch); update_free_zone(&new_contents); @@ -651,15 +642,18 @@ static void duplicate_ddns_q(zone_t *zone, zone_t *old_zone) /*!< Replans DDNS event. */ static void replan_update(zone_t *zone, zone_t *old_zone) { - pthread_mutex_lock(&old_zone->ddns_lock); + pthread_spin_lock(&old_zone->ddns_lock); - if (!EMPTY_LIST(old_zone->ddns_queue)) { + const bool empty = EMPTY_LIST(old_zone->ddns_queue); + if (!empty) { duplicate_ddns_q(zone, (zone_t *)old_zone); - // \todo #254 Old zone *must* have the event planned, but it was not always so + } + + pthread_spin_unlock(&old_zone->ddns_lock); + + if (!empty) { zone_events_schedule(zone, ZONE_EVENT_UPDATE, ZONE_EVENT_NOW); } - - pthread_mutex_unlock(&old_zone->ddns_lock); } /*!< Replans DNSSEC event. Not whole resign needed, \todo #247 */ @@ -824,7 +818,7 @@ static void event_wrap(task_t *task) 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)\n", info->name, + log_zone_error(zone->name, "zone %s failed (%s)", info->name, knot_strerror(result)); } diff --git a/src/knot/zone/node.c b/src/knot/zone/node.c index 33daa4eee7110e28ec58f7e93693f507bc8e9fff..ba8640625d960daa107bd852d3aa829506bb051d 100644 --- a/src/knot/zone/node.c +++ b/src/knot/zone/node.c @@ -46,19 +46,21 @@ static int rr_data_from(const knot_rrset_t *rrset, struct rr_data *data, mm_ctx_ } /*! \brief Adds RRSet to node directly. */ -static int add_rrset_no_merge(zone_node_t *node, const knot_rrset_t *rrset) +static int add_rrset_no_merge(zone_node_t *node, const knot_rrset_t *rrset, + mm_ctx_t *mm) { if (node == NULL) { return KNOT_EINVAL; } + const size_t prev_nlen = node->rrset_count * sizeof(struct rr_data); const size_t nlen = (node->rrset_count + 1) * sizeof(struct rr_data); - void *p = realloc(node->rrs, nlen); + void *p = mm_realloc(mm, node->rrs, nlen, prev_nlen); if (p == NULL) { return KNOT_ENOMEM; } node->rrs = p; - int ret = rr_data_from(rrset, node->rrs + node->rrset_count, NULL); + int ret = rr_data_from(rrset, node->rrs + node->rrset_count, mm); if (ret != KNOT_EOK) { return ret; } @@ -82,9 +84,9 @@ static bool ttl_error(struct rr_data *node_data, const knot_rrset_t *rrset) return inserted_ttl != node_ttl; } -zone_node_t *node_new(const knot_dname_t *owner) +zone_node_t *node_new(const knot_dname_t *owner, mm_ctx_t *mm) { - zone_node_t *ret = malloc(sizeof(zone_node_t)); + zone_node_t *ret = mm_alloc(mm, sizeof(zone_node_t)); if (ret == NULL) { ERR_ALLOC_FAILED; return NULL; @@ -92,9 +94,9 @@ zone_node_t *node_new(const knot_dname_t *owner) memset(ret, 0, sizeof(*ret)); if (owner) { - ret->owner = knot_dname_copy(owner, NULL); + ret->owner = knot_dname_copy(owner, mm); if (ret->owner == NULL) { - free(ret); + mm_free(mm, ret); return NULL; } } @@ -105,7 +107,7 @@ zone_node_t *node_new(const knot_dname_t *owner) return ret; } -void node_free_rrsets(zone_node_t *node) +void node_free_rrsets(zone_node_t *node, mm_ctx_t *mm) { if (node == NULL) { return; @@ -118,30 +120,30 @@ void node_free_rrsets(zone_node_t *node) node->rrset_count = 0; } -void node_free(zone_node_t **node) +void node_free(zone_node_t **node, mm_ctx_t *mm) { if (node == NULL || *node == NULL) { return; } if ((*node)->rrs != NULL) { - free((*node)->rrs); + mm_free(mm, (*node)->rrs); } - knot_dname_free(&(*node)->owner, NULL); + knot_dname_free(&(*node)->owner, mm); - free(*node); + mm_free(mm, *node); *node = NULL; } -zone_node_t *node_shallow_copy(const zone_node_t *src) +zone_node_t *node_shallow_copy(const zone_node_t *src, mm_ctx_t *mm) { if (src == NULL) { return NULL; } // create new node - zone_node_t *dst = node_new(src->owner); + zone_node_t *dst = node_new(src->owner, mm); if (dst == NULL) { return NULL; } @@ -151,9 +153,9 @@ zone_node_t *node_shallow_copy(const zone_node_t *src) // copy RRSets dst->rrset_count = src->rrset_count; size_t rrlen = sizeof(struct rr_data) * src->rrset_count; - dst->rrs = malloc(rrlen); + dst->rrs = mm_alloc(mm, rrlen); if (dst->rrs == NULL) { - node_free(&dst); + node_free(&dst, mm); return NULL; } memcpy(dst->rrs, src->rrs, rrlen); @@ -166,7 +168,7 @@ zone_node_t *node_shallow_copy(const zone_node_t *src) return dst; } -int node_add_rrset(zone_node_t *node, const knot_rrset_t *rrset) +int node_add_rrset(zone_node_t *node, const knot_rrset_t *rrset, mm_ctx_t *mm) { if (node == NULL || rrset == NULL) { return KNOT_EINVAL; @@ -177,7 +179,7 @@ int node_add_rrset(zone_node_t *node, const knot_rrset_t *rrset) struct rr_data *node_data = &node->rrs[i]; const bool ttl_err = ttl_error(node_data, rrset); int ret = knot_rdataset_merge(&node_data->rrs, - &rrset->rrs, NULL); + &rrset->rrs, mm); if (ret != KNOT_EOK) { return ret; } else { @@ -187,7 +189,7 @@ int node_add_rrset(zone_node_t *node, const knot_rrset_t *rrset) } // New RRSet (with one RR) - return add_rrset_no_merge(node, rrset); + return add_rrset_no_merge(node, rrset, mm); } void node_remove_rdataset(zone_node_t *node, uint16_t type) @@ -283,3 +285,8 @@ bool node_rrtype_exists(const zone_node_t *node, uint16_t type) { return node_rdataset(node, type) != NULL; } + +bool node_empty(const zone_node_t *node) +{ + return node == NULL || node->rrset_count == 0; +} diff --git a/src/knot/zone/node.h b/src/knot/zone/node.h index bca15bc33e6da6e4530f89c902ce1002f46e7f17..7cca34ad2d9d9432f030d9f96e773e2a0efa9f3b 100644 --- a/src/knot/zone/node.h +++ b/src/knot/zone/node.h @@ -86,18 +86,20 @@ enum node_flags { * \brief Creates and initializes new node structure. * * \param owner Node's owner, will be duplicated. + * \param mm Memory context to use. * * \return Newly created node or NULL if an error occured. */ -zone_node_t *node_new(const knot_dname_t *owner); +zone_node_t *node_new(const knot_dname_t *owner, mm_ctx_t *mm); /*! * \brief Destroys allocated data within the node * structure, but not the node itself. * - * \param node Node that contains data to be destroyed. + * \param node Node that contains data to be destroyed. + * \param mm Memory context to use. */ -void node_free_rrsets(zone_node_t *node); +void node_free_rrsets(zone_node_t *node, mm_ctx_t *mm); /*! * \brief Destroys the node structure. @@ -105,18 +107,20 @@ void node_free_rrsets(zone_node_t *node); * Does not destroy the data within the node. * Also sets the given pointer to NULL. * - * \param node Node to be destroyed. + * \param node Node to be destroyed. + * \param mm Memory context to use. */ -void node_free(zone_node_t **node); +void node_free(zone_node_t **node, mm_ctx_t *mm); /*! * \brief Creates a shallow copy of node structure, RR data are shared. * * \param src Source of the copy. + * \param mm Memory context to use. * * \return Copied node if success, NULL otherwise. */ -zone_node_t *node_shallow_copy(const zone_node_t *src); +zone_node_t *node_shallow_copy(const zone_node_t *src, mm_ctx_t *mm); /* ----------------------- Data addition/removal -----------------------------*/ @@ -129,7 +133,7 @@ zone_node_t *node_shallow_copy(const zone_node_t *src); * * \return KNOT_E* */ -int node_add_rrset(zone_node_t *node, const knot_rrset_t *rrset); +int node_add_rrset(zone_node_t *node, const knot_rrset_t *rrset, mm_ctx_t *mm); /*! * \brief Removes data for given RR type from node. @@ -194,6 +198,16 @@ bool node_rrtype_is_signed(const zone_node_t *node, uint16_t type); */ bool node_rrtype_exists(const zone_node_t *node, uint16_t type); +/*! + * \brief Checks whether node is empty. Node is empty when NULL or when no + * RRSets are in it. + * + * \param node Node to check in. + * + * \return True/False. + */ +bool node_empty(const zone_node_t *node); + /* -------------------- Inline RRSet initializations ------------------------ */ /*! diff --git a/src/knot/zone/semantic-check.c b/src/knot/zone/semantic-check.c index 219f601da8c0f34dd619324f2553b0bf397e3934..e93bbc774e0618119bd7efe15728fab42ae2befa 100644 --- a/src/knot/zone/semantic-check.c +++ b/src/knot/zone/semantic-check.c @@ -39,77 +39,78 @@ #include "knot/zone/semantic-check.h" static char *error_messages[(-ZC_ERR_UNKNOWN) + 1] = { - [-ZC_ERR_MISSING_SOA] = "SOA record missing in zone!", - [-ZC_ERR_MISSING_NS_DEL_POINT] = "NS record missing in zone apex or in " - "delegation point!", - [-ZC_ERR_TTL_MISMATCH] = "RRSet TTLs mismatched!", + [-ZC_ERR_MISSING_SOA] = + "SOA record missing in zone", + [-ZC_ERR_MISSING_NS_DEL_POINT] = + "NS record missing in zone apex or in delegation point", + [-ZC_ERR_TTL_MISMATCH] = + "RRSet TTLs mismatched", [-ZC_ERR_RRSIG_RDATA_TYPE_COVERED] = - "RRSIG: Type covered RDATA field is wrong!", + "RRSIG: type covered RDATA field is wrong", [-ZC_ERR_RRSIG_RDATA_TTL] = - "RRSIG: TTL RDATA field is wrong!", + "RRSIG: TTL RDATA field is wrong", [-ZC_ERR_RRSIG_RDATA_EXPIRATION] = - "RRSIG: Expired signature!", + "RRSIG: expired signature", [-ZC_ERR_RRSIG_RDATA_LABELS] = - "RRSIG: Labels RDATA field is wrong!", + "RRSIG: labels RDATA field is wrong", [-ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER] = - "RRSIG: Signer name is different than in DNSKEY!", + "RRSIG: signer name is different than in DNSKEY", [-ZC_ERR_RRSIG_NO_DNSKEY] = - "RRSIG: Missing DNSKEY for RRSIG!", + "RRSIG: missing DNSKEY for RRSIG", [-ZC_ERR_RRSIG_RDATA_SIGNED_WRONG] = - "RRSIG: Key error!", + "RRSIG: key error", [-ZC_ERR_RRSIG_NO_RRSIG] = - "RRSIG: No RRSIG!", + "RRSIG: no RRSIG", [-ZC_ERR_RRSIG_SIGNED] = - "RRSIG: Signed RRSIG!", + "RRSIG: signed RRSIG", [-ZC_ERR_RRSIG_OWNER] = - "RRSIG: Owner name RDATA field is wrong!", + "RRSIG: owner name RDATA field is wrong", [-ZC_ERR_RRSIG_CLASS] = - "RRSIG: Class is wrong!", + "RRSIG: class is wrong", [-ZC_ERR_RRSIG_TTL] = - "RRSIG: TTL is wrong!", + "RRSIG: TTL is wrong", [-ZC_ERR_NO_NSEC] = - "NSEC: Missing NSEC record", + "NSEC: missing record", [-ZC_ERR_NSEC_RDATA_BITMAP] = - "NSEC: Wrong NSEC bitmap!", + "NSEC: wrong bitmap", [-ZC_ERR_NSEC_RDATA_MULTIPLE] = - "NSEC: Multiple NSEC records!", + "NSEC: multiple records", [-ZC_ERR_NSEC_RDATA_CHAIN] = - "NSEC: NSEC chain is not coherent!", + "NSEC: chain is not coherent", [-ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC] = - "NSEC: NSEC chain is not cyclic!", + "NSEC: chain is not cyclic", [-ZC_ERR_NSEC3_UNSECURED_DELEGATION] = - "NSEC3: Zone contains unsecured delegation!", + "NSEC3: zone contains unsecured delegation", [-ZC_ERR_NSEC3_NOT_FOUND] = - "NSEC3: Could not find previous NSEC3 record in the zone!", + "NSEC3: could not find previous NSEC3 record in the zone", [-ZC_ERR_NSEC3_UNSECURED_DELEGATION_OPT] = - "NSEC3: Unsecured delegation is not part " - "of the Opt-Out span!", + "NSEC3: unsecured delegation is not part of the opt-out span", [-ZC_ERR_NSEC3_RDATA_TTL] = - "NSEC3: Original TTL RDATA field is wrong!", + "NSEC3: original TTL RDATA field is wrong", [-ZC_ERR_NSEC3_RDATA_CHAIN] = - "NSEC3: NSEC3 chain is not coherent!", + "NSEC3: chain is not coherent", [-ZC_ERR_NSEC3_RDATA_BITMAP] = - "NSEC3: NSEC3 bitmap error!", + "NSEC3: wrong bitmap", [-ZC_ERR_NSEC3_EXTRA_RECORD] = - "NSEC3: NSEC3 node contains extra record. This is valid, however Knot " - "will not serve this record properly.", + "NSEC3: node contains extra record, unsupported", [-ZC_ERR_CNAME_EXTRA_RECORDS] = - "CNAME: Node with CNAME record has other records!", + "CNAME: node contains other records", [-ZC_ERR_DNAME_CHILDREN] = - "DNAME: Node with DNAME record has children!", + "DNAME: node has children", [-ZC_ERR_CNAME_EXTRA_RECORDS_DNSSEC] = - "CNAME: Node with CNAME record has other " - "records than RRSIG and NSEC/NSEC3!", - [-ZC_ERR_CNAME_MULTIPLE] = "CNAME: Multiple CNAME records!", - [-ZC_ERR_DNAME_MULTIPLE] = "DNAME: Multiple DNAME records!", - [-ZC_ERR_CNAME_WILDCARD_SELF] = "CNAME wildcard " - "pointing to itself!", - [-ZC_ERR_DNAME_WILDCARD_SELF] = "DNAME wildcard " - "pointing to itself!", + "CNAME: node contains other records than RRSIG and NSEC/NSEC3", + [-ZC_ERR_CNAME_MULTIPLE] = + "CNAME: multiple records", + [-ZC_ERR_DNAME_MULTIPLE] = + "DNAME: multiple records", + [-ZC_ERR_CNAME_WILDCARD_SELF] = + "CNAME: wildcard pointing to itself", + [-ZC_ERR_DNAME_WILDCARD_SELF] = + "DNAME: wildcard pointing to itself", /* ^ | Important errors (to be logged on first occurence and counted) */ @@ -118,9 +119,9 @@ static char *error_messages[(-ZC_ERR_UNKNOWN) + 1] = { specified otherwise */ [-ZC_ERR_GLUE_NODE] = - "GLUE: Node with glue record missing!", + "GLUE: node with glue record missing", [-ZC_ERR_GLUE_RECORD] = - "GLUE: Record with glue address missing!", + "GLUE: record with glue address missing", }; void err_handler_init(err_handler_t *h) @@ -168,12 +169,12 @@ static void log_error_from_node(err_handler_t *handler, const knot_dname_t *zone_name = zone->apex->owner; if (error > (int)ZC_ERR_GLUE_RECORD) { - log_zone_warning(zone_name, "sematic check: unknown error\n"); + log_zone_warning(zone_name, "sematic check: unknown error"); return; } if (node == NULL) { - log_zone_warning(zone_name, "semantic check: %d warnings, error %s\n", + log_zone_warning(zone_name, "semantic check: %d warnings, error %s", handler->errors[-error], error_messages[-error]); return; } @@ -183,7 +184,7 @@ static void log_error_from_node(err_handler_t *handler, char *name = knot_dname_to_str(node->owner); const char *errmsg = error_messages[-error]; - log_zone_warning(zone_name, "semantic check: node '%s': %s%s%s\n", + log_zone_warning(zone_name, "semantic check: node '%s': %s%s%s", name, errmsg ? errmsg : "unknown error", data ? " " : "", @@ -1075,7 +1076,7 @@ void log_cyclic_errors_in_zone(err_handler_t *handler, apex->owner); if (next_dname == NULL) { log_zone_warning(zone->apex->owner, "sematic check: " - "could not create new dname\n"); + "could not create new dname"); return; } diff --git a/src/knot/zone/zone-load.c b/src/knot/zone/zone-load.c index c6a3f2a6f74050317cc6130412fe438878b3190f..f5e2f3a1e5a44882f04b8581cc09a2b9c32ec139 100644 --- a/src/knot/zone/zone-load.c +++ b/src/knot/zone/zone-load.c @@ -63,7 +63,7 @@ int zone_load_check(zone_contents_t *contents, conf_zone_t *zone_config) if (zone_contents_is_signed(contents)) { if (conf()->max_udp_payload < KNOT_EDNS_MIN_DNSSEC_PAYLOAD) { log_zone_warning(zone_name, "EDNS payload size is " - "lower than %u bytes for DNSSEC zone", + "lower than %u bytes for DNSSEC zone", KNOT_EDNS_MIN_DNSSEC_PAYLOAD); conf()->max_udp_payload = KNOT_EDNS_MIN_DNSSEC_PAYLOAD; } @@ -73,7 +73,7 @@ int zone_load_check(zone_contents_t *contents, conf_zone_t *zone_config) int result = zone_contents_load_nsec3param(contents); if (result != KNOT_EOK) { log_zone_error(zone_name, "NSEC3 signed zone has invalid or no " - "NSEC3PARAM record\n"); + "NSEC3PARAM record"); return result; } @@ -110,7 +110,7 @@ int zone_load_journal(zone_t *zone, zone_contents_t *contents) /* Apply changesets. */ ret = apply_changesets_directly(contents, &chgs); - log_zone_info(zone->name, "serial %u -> %u: %s\n", + log_zone_info(zone->name, "serial %u -> %u: %s", serial, zone_contents_serial(contents), knot_strerror(ret)); @@ -169,18 +169,17 @@ int zone_load_post(zone_contents_t *contents, zone_t *zone, uint32_t *dnssec_ref ret = zone_contents_create_diff(zone->contents, contents, &change); if (ret == KNOT_ENODIFF) { log_zone_warning(zone->name, "zone file changed, " - "but serial didn't, won't " - "create journal entry\n"); + "but serial didn't, won't create " + "create journal entry"); ret = KNOT_EOK; } else if (ret == KNOT_ERANGE) { log_zone_warning(zone->name, "zone file changed, " "but serial is lower than before, " - "IXFR history will be lost\n"); + "IXFR history will be lost"); ret = KNOT_EOK; } else if (ret != KNOT_EOK) { log_zone_error(zone->name, "failed to calculate " - "differences from the zone " - "file update: %s\n", + "differences from the zone file update: %s", knot_strerror(ret)); return ret; } diff --git a/src/knot/zone/zone-tree.c b/src/knot/zone/zone-tree.c index 7c379e77e2b0cc2b94c6f6e9ff8b570e1a4781b7..ef601ec07996eaa232737f339bc6d9ce90efef71 100644 --- a/src/knot/zone/zone-tree.c +++ b/src/knot/zone/zone-tree.c @@ -192,6 +192,40 @@ dbg_zone_exec_detail( /*----------------------------------------------------------------------------*/ +zone_node_t *zone_tree_get_next(zone_tree_t *tree, + const knot_dname_t *owner) +{ + if (tree == NULL || owner == NULL) { + return NULL; + } + + uint8_t lf[KNOT_DNAME_MAXLEN]; + knot_dname_lf(lf, owner, NULL); + + value_t *fval = NULL; + zone_node_t *n = NULL; + (void)hattrie_find_next(tree, (char*)lf + 1, *lf, &fval); + if (fval == NULL) { + /* Return first node. */ + hattrie_iter_t *it = hattrie_iter_begin(tree, true); + if (it == NULL) { + return NULL; + } + 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) { + return zone_tree_get_next(tree, n->owner); + } else { + return n; + } +} + +/*----------------------------------------------------------------------------*/ + int zone_tree_remove(zone_tree_t *tree, const knot_dname_t *owner, zone_node_t **removed) @@ -282,7 +316,7 @@ static int zone_tree_free_node(zone_node_t **node, void *data) { UNUSED(data); if (node) { - node_free(node); + node_free(node, NULL); } return KNOT_EOK; } diff --git a/src/knot/zone/zone.c b/src/knot/zone/zone.c index d26c506d4171a7e1a2927e7accfe7fb78e5049c6..119107252c62c8f56188aaa25a814ca72368dcae 100644 --- a/src/knot/zone/zone.c +++ b/src/knot/zone/zone.c @@ -73,7 +73,8 @@ zone_t* zone_new(conf_zone_t *conf) zone->conf = conf; // DDNS - pthread_mutex_init(&zone->ddns_lock, 0); + pthread_spin_init(&zone->ddns_lock, 0); + zone->ddns_queue_size = 0; init_list(&zone->ddns_queue); // Initialize events @@ -95,7 +96,7 @@ void zone_free(zone_t **zone_ptr) knot_dname_free(&zone->name, NULL); free_ddns_queue(zone); - pthread_mutex_destroy(&zone->ddns_lock); + pthread_spin_destroy(&zone->ddns_lock); /* Free assigned config. */ conf_free_zone(zone->conf); @@ -116,7 +117,7 @@ int zone_change_store(zone_t *zone, changeset_t *change) int ret = journal_store_changeset(change, conf->ixfr_db, conf->ixfr_fslimit); if (ret == KNOT_EBUSY) { - log_zone_notice(zone->name, "zone journal is full, flushing\n"); + log_zone_notice(zone->name, "zone journal is full, flushing"); /* Transaction rolled back, journal released, we may flush. */ ret = zone_flush_journal(zone); @@ -139,7 +140,7 @@ int zone_changes_store(zone_t *zone, list_t *chgs) int ret = journal_store_changesets(chgs, conf->ixfr_db, conf->ixfr_fslimit); if (ret == KNOT_EBUSY) { - log_zone_notice(zone->name, "journal is full, flushing\n"); + log_zone_notice(zone->name, "journal is full, flushing"); /* Transaction rolled back, journal released, we may flush. */ ret = zone_flush_journal(zone); @@ -217,10 +218,10 @@ int zone_flush_journal(zone_t *zone) conf_zone_t *conf = zone->conf; int ret = zonefile_write(conf->file, contents, from); if (ret == KNOT_EOK) { - log_zone_info(zone->name, "zone file updated, serial %u -> %u\n", + log_zone_info(zone->name, "zone file updated, serial %u -> %u", zone->zonefile_serial, serial_to); } else { - log_zone_warning(zone->name, "failed to update zone file: %s\n", + log_zone_warning(zone->name, "failed to update zone file: %s", knot_strerror(ret)); return ret; } @@ -228,7 +229,7 @@ int zone_flush_journal(zone_t *zone) /* Update zone version. */ struct stat st; if (stat(zone->conf->file, &st) < 0) { - log_zone_warning(zone->name, "failed to update zone file: %s\n", + log_zone_warning(zone->name, "failed to update zone file: %s", knot_strerror(KNOT_EACCES)); return KNOT_EACCES; } @@ -266,12 +267,13 @@ int zone_update_enqueue(zone_t *zone, knot_pkt_t *pkt, struct process_query_para return ret; } - pthread_mutex_lock(&zone->ddns_lock); + pthread_spin_lock(&zone->ddns_lock); /* Enqueue created request. */ add_tail(&zone->ddns_queue, (node_t *)req); + ++zone->ddns_queue_size; - pthread_mutex_unlock(&zone->ddns_lock); + pthread_spin_unlock(&zone->ddns_lock); /* Schedule UPDATE event. */ zone_events_schedule(zone, ZONE_EVENT_UPDATE, ZONE_EVENT_NOW); @@ -279,24 +281,25 @@ int zone_update_enqueue(zone_t *zone, knot_pkt_t *pkt, struct process_query_para return KNOT_EOK; } -struct knot_request_data *zone_update_dequeue(zone_t *zone) +void zone_update_dequeue(zone_t *zone, list_t *updates, size_t *update_count) { if (zone == NULL) { - return NULL; + return; } - pthread_mutex_lock(&zone->ddns_lock); - if (knot_unlikely(EMPTY_LIST(zone->ddns_queue))) { + pthread_spin_lock(&zone->ddns_lock); + if (EMPTY_LIST(zone->ddns_queue)) { /* Lost race during reload. */ - pthread_mutex_unlock(&zone->ddns_lock); - return NULL; + pthread_spin_unlock(&zone->ddns_lock); + return; } - struct knot_request_data *ret = HEAD(zone->ddns_queue); - rem_node((node_t *)ret); - pthread_mutex_unlock(&zone->ddns_lock); + *updates = zone->ddns_queue; + *update_count = zone->ddns_queue_size; + init_list(&zone->ddns_queue); + zone->ddns_queue_size = 0; - return ret; + pthread_spin_unlock(&zone->ddns_lock); } bool zone_transfer_needed(const zone_t *zone, const knot_pkt_t *pkt) diff --git a/src/knot/zone/zone.h b/src/knot/zone/zone.h index 27f32060d2d39f181a91d46ad68c2e0c9d6b1678..1b849925c982e6818f7ae50f41b7846a4745ca0d 100644 --- a/src/knot/zone/zone.h +++ b/src/knot/zone/zone.h @@ -58,7 +58,8 @@ typedef struct zone_t zone_flag_t flags; /*! \brief DDNS queue and lock. */ - pthread_mutex_t ddns_lock; + pthread_spinlock_t ddns_lock; + size_t ddns_queue_size; list_t ddns_queue; /*! \brief Zone events. */ @@ -115,7 +116,7 @@ int zone_flush_journal(zone_t *zone); int zone_update_enqueue(zone_t *zone, knot_pkt_t *pkt, struct process_query_param *param); /*! \brief Dequeue UPDATE request. */ -struct knot_request_data *zone_update_dequeue(zone_t *zone); +void zone_update_dequeue(zone_t *zone, list_t *updates, size_t *update_count); /*! \brief Returns true if final SOA in transfer has newer serial than zone */ bool zone_transfer_needed(const zone_t *zone, const knot_pkt_t *pkt); diff --git a/src/knot/zone/zonedb-load.c b/src/knot/zone/zonedb-load.c index 249deccef7e70fe54cfa9d9535e10e7249664d73..6bb1f6da7eed8a15cc38505d388c9eace6b604cc 100644 --- a/src/knot/zone/zonedb-load.c +++ b/src/knot/zone/zonedb-load.c @@ -109,7 +109,7 @@ static void log_zone_load_info(const zone_t *zone, const char *zone_name, serial = knot_soa_serial(soa); } - log_zone_info(zone->name, "zone %s, serial %u\n", action, serial); + log_zone_info(zone->name, "zone %s, serial %u", action, serial); } /*! @@ -206,7 +206,7 @@ static knot_zonedb_t *create_zonedb(const conf_t *conf, server_t *server) zone_t *zone = create_zone(zone_config, server, old_zone); if (!zone) { log_zone_str_error(zone_config->name, - "zone cannot be created\n"); + "zone cannot be created"); conf_free_zone(zone_config); continue; } @@ -269,7 +269,7 @@ int zonedb_reload(const conf_t *conf, struct server_t *server) be required. */ knot_zonedb_t *db_new = create_zonedb(conf, server); if (db_new == NULL) { - log_error("failed to create new zone database\n"); + log_error("failed to create new zone database"); return KNOT_ENOMEM; } diff --git a/src/knot/zone/zonefile.c b/src/knot/zone/zonefile.c index adb04399d4eccd60383feae442e41f9d2e0a0106..0924c00f1059062fc4356e3ae2085ec9733cec38 100644 --- a/src/knot/zone/zonefile.c +++ b/src/knot/zone/zonefile.c @@ -35,7 +35,6 @@ #include "knot/dnssec/zone-nsec.h" #include "knot/other/debug.h" #include "knot/zone/zonefile.h" -#include "zscanner/loader.h" #include "libknot/rdata.h" #include "knot/zone/zone-dump.h" @@ -46,12 +45,12 @@ void process_error(zs_scanner_t *s) if (s->stop == true) { log_zone_error(zname, "fatal error in zone file %s:%"PRIu64": %s, " - "stopping zone loading.\n", - s->file_name, s->line_counter, + "stopping zone loading.", + s->file.name, s->line_counter, zs_strerror(s->error_code)); } else { - log_zone_error(zname,"error in zone file %s:%"PRIu64": %s\n", - s->file_name, s->line_counter, + log_zone_error(zname,"error in zone file %s:%" PRIu64 ": %s", + s->file.name, s->line_counter, zs_strerror(s->error_code)); } } @@ -68,7 +67,7 @@ static bool handle_err(zcreator_t *zc, const zone_node_t *node, const knot_dname_t *zname = zc->z->apex->owner; char *rrname = rr ? knot_dname_to_str(rr->owner) : NULL; if (ret == KNOT_EOUTOFZONE) { - log_zone_warning(zname, "ignoring out-of-zone data for %s\n", + log_zone_warning(zname, "ignoring out-of-zone data for %s", rrname ? rrname : "unknown"); free(rrname); return true; @@ -79,7 +78,7 @@ static bool handle_err(zcreator_t *zc, const zone_node_t *node, // Fail if we're the master for this zone. return !master; } else { - log_zone_error(zname, "cannot process record %s, stopping\n", + log_zone_error(zname, "cannot process record %s, stopping", rrname ? rrname : "unknown"); free(rrname); return false; @@ -148,7 +147,7 @@ int zcreator_step(zcreator_t *zc, const knot_rrset_t *rr) } /*! \brief Creates RR from parser input, passes it to handling function. */ -static void loader_process(zs_scanner_t *scanner) +static void scanner_process(zs_scanner_t *scanner) { zcreator_t *zc = scanner->data; if (zc->ret != KNOT_EOK) { @@ -168,7 +167,7 @@ static void loader_process(zs_scanner_t *scanner) if (ret != KNOT_EOK) { char *rr_name = knot_dname_to_str(rr.owner); log_zone_error(zc->z->apex->owner, "file %s:%"PRIu64": " - "can't add RDATA for '%s'\n", scanner->file_name, + "can't add RDATA for '%s'", scanner->file.name, scanner->line_counter, rr_name); free(rr_name); knot_dname_free(&owner, NULL); @@ -228,11 +227,10 @@ int zonefile_open(zloader_t *loader, const char *source, const char *origin, /* Create file loader. */ memset(loader, 0, sizeof(zloader_t)); - loader->file_loader = zs_loader_create(source, origin, - KNOT_CLASS_IN, 3600, - loader_process, process_error, - zc); - if (loader->file_loader == NULL) { + loader->scanner = zs_scanner_create(origin, KNOT_CLASS_IN, 3600, + scanner_process, process_error, + zc); + if (loader->scanner == NULL) { free(zc); return KNOT_ERROR; } @@ -257,29 +255,29 @@ zone_contents_t *zonefile_load(zloader_t *loader) const knot_dname_t *zname = zc->z->apex->owner; assert(zc); - int ret = zs_loader_process(loader->file_loader); - if (ret != ZS_OK) { - log_zone_error(zname, "zone file '%s' could not be loaded: %s\n", + int ret = zs_scanner_parse_file(loader->scanner, loader->source); + if (ret != 0) { + log_zone_error(zname, "zone file '%s' could not be loaded: %s", loader->source, zs_strerror(ret)); goto fail; } if (zc->ret != KNOT_EOK) { - log_zone_error(zname, "zone file '%s' could not be loaded: %s\n", + log_zone_error(zname, "zone file '%s' could not be loaded: %s", loader->source, knot_strerror(zc->ret)); goto fail; } - if (loader->file_loader->scanner->error_counter > 0) { + if (loader->scanner->error_counter > 0) { log_zone_error(zname, "zone file '%s' could not be loaded due " - "to %"PRIu64" errors encountered\n", + "to %"PRIu64" errors encountered", loader->source, - loader->file_loader->scanner->error_counter); + loader->scanner->error_counter); goto fail; } if (!node_rrtype_exists(loader->creator->z->apex, KNOT_RRTYPE_SOA)) { - log_zone_error(zname, "no SOA record in the zone file '%s'\n", + log_zone_error(zname, "no SOA record in the zone file '%s'", loader->source); goto fail; } @@ -290,7 +288,7 @@ zone_contents_t *zonefile_load(zloader_t *loader) int kret = zone_contents_adjust_full(zc->z, &first_nsec3_node, &last_nsec3_node); if (kret != KNOT_EOK) { - log_zone_error(zname, "failed to finalize zone contents: %s\n", + log_zone_error(zname, "failed to finalize zone contents: %s", knot_strerror(kret)); goto fail; } @@ -313,7 +311,7 @@ zone_contents_t *zonefile_load(zloader_t *loader) zone_do_sem_checks(zc->z, check_level, &err_handler, first_nsec3_node, last_nsec3_node); - log_zone_info(zname, "semantic checks completed\n"); + log_zone_info(zname, "semantic checks completed"); } return zc->z; @@ -375,14 +373,14 @@ int zonefile_write(const char *path, zone_contents_t *zone, FILE *f = fdopen(fd, "w"); if (f == NULL) { - log_zone_warning(zname, "failed to open zone master file\n"); + log_zone_warning(zname, "failed to open zone master file"); unlink(new_fname); free(new_fname); return KNOT_ERROR; } if (zone_dump_text(zone, from, f) != KNOT_EOK) { - log_zone_warning(zname, "failed to save the transferred zone to '%s'\n", + log_zone_warning(zname, "failed to save the transferred zone to '%s'", new_fname); fclose(f); unlink(new_fname); @@ -399,7 +397,7 @@ int zonefile_write(const char *path, zone_contents_t *zone, int ret = rename(new_fname, path); if (ret < 0 && ret != EEXIST) { log_zone_warning(zname, "failed to replace old zone file '%s' " - "with a new zone file '%s'\n", path, new_fname); + "with a new zone file '%s'", path, new_fname); unlink(new_fname); free(new_fname); return KNOT_ERROR; @@ -415,7 +413,7 @@ void zonefile_close(zloader_t *loader) return; } - zs_loader_free(loader->file_loader); + zs_scanner_free(loader->scanner); free(loader->source); free(loader->origin); diff --git a/src/knot/zone/zonefile.h b/src/knot/zone/zonefile.h index 0d8f0c268f5358ac822dfe0edb8a35b4e3b3905f..fc0b52c3d6d034cccbf20d39d2fd9212b71bfc39 100644 --- a/src/knot/zone/zonefile.h +++ b/src/knot/zone/zonefile.h @@ -31,7 +31,7 @@ #include "knot/zone/zone.h" #include "knot/zone/semantic-check.h" -#include "zscanner/zscanner.h" +#include "zscanner/scanner.h" /*! * \brief Zone creator structure. */ @@ -50,7 +50,7 @@ typedef struct zloader_t { char *origin; /*!< Zone's origin string. */ bool semantic_checks; /*!< Do semantic checks. */ err_handler_t *err_handler; /*!< Semantic checks error handler. */ - zs_loader_t *file_loader; /*!< Scanner's file loader. */ + zs_scanner_t *scanner; /*!< Zone scanner. */ zcreator_t *creator; /*!< Loader context. */ } zloader_t; diff --git a/src/libknot/dnssec/key.c b/src/libknot/dnssec/key.c index bb0db1eddf564139929037f88d6a45e31ebc1566..dc6fff2894e68b6ae1f16264ebcbc85f262c3b56 100644 --- a/src/libknot/dnssec/key.c +++ b/src/libknot/dnssec/key.c @@ -34,7 +34,7 @@ #include "libknot/dnssec/key.h" #include "libknot/dnssec/sig0.h" #include "libknot/rrtype/tsig.h" -#include "zscanner/zscanner.h" +#include "zscanner/scanner.h" /*! * \brief Calculates keytag for RSA/MD5 algorithm. @@ -115,24 +115,18 @@ static int get_key_info_from_public_key(const char *filename, return KNOT_KEY_EPUBLIC_KEY_OPEN; } - zs_scanner_t *scanner = zs_scanner_create(filename, ".", KNOT_CLASS_IN, - 0, NULL, NULL, NULL); + bool scan_done = false; + + zs_scanner_t *scanner = zs_scanner_create(".", KNOT_CLASS_IN, 0, + key_scan_set_done, + key_scan_set_done, + (void *)&scan_done); if (!scanner) { fclose(keyfile); return KNOT_ENOMEM; } - bool scan_done = false; bool last_block = false; - - scanner->process_record = key_scan_set_done; - scanner->process_error = key_scan_set_done; - scanner->default_ttl = 0; - scanner->default_class = KNOT_CLASS_IN; - scanner->zone_origin[0] = '\0'; - scanner->zone_origin_length = 1; - scanner->data = (void *)&scan_done; - char *buffer = NULL; size_t buffer_size; ssize_t read; @@ -144,8 +138,8 @@ static int get_key_info_from_public_key(const char *filename, last_block = true; read = 0; } - result = zs_scanner_process(buffer, buffer + read, last_block, - scanner); + result = zs_scanner_parse(scanner, buffer, buffer + read, + last_block); } free(buffer); diff --git a/src/libknot/dnssec/rrset-sign.c b/src/libknot/dnssec/rrset-sign.c index 65c0b53d3c3f9af8a675e93e923468fd92a6d365..ea690cefbdb1012dab88f64fe8275b3aaf4fd14c 100644 --- a/src/libknot/dnssec/rrset-sign.c +++ b/src/libknot/dnssec/rrset-sign.c @@ -18,17 +18,17 @@ #include <stdbool.h> #include <stdint.h> #include <stdlib.h> -#include "libknot/errcode.h" -#include "libknot/descriptor.h" #include "libknot/common.h" +#include "libknot/descriptor.h" #include "libknot/dnssec/key.h" #include "libknot/dnssec/policy.h" #include "libknot/dnssec/rrset-sign.h" #include "libknot/dnssec/sign.h" +#include "libknot/errcode.h" +#include "libknot/packet/wire.h" #include "libknot/rrset.h" #include "libknot/rrtype/rrsig.h" -#define MAX_RR_WIREFORMAT_SIZE (64 * 1024) #define RRSIG_RDATA_SIGNER_OFFSET 18 /*- Creating of RRSIGs -------------------------------------------------------*/ @@ -139,23 +139,19 @@ static int sign_ctx_add_records(knot_dnssec_sign_context_t *ctx, const knot_rrset_t *covered) { // huge block of rrsets can be optionally created - uint8_t *rrwf = malloc(MAX_RR_WIREFORMAT_SIZE); + uint8_t *rrwf = malloc(KNOT_WIRE_MAX_PKTSIZE); if (!rrwf) { return KNOT_ENOMEM; } - int result = KNOT_EOK; - - size_t rr_wire_size = 0; - uint16_t rr_count = 0; - result = knot_rrset_to_wire(covered, rrwf, &rr_wire_size, - MAX_RR_WIREFORMAT_SIZE, &rr_count, NULL); - if (result != KNOT_EOK || rr_count != covered->rrs.rr_count) { + int written = knot_rrset_to_wire(covered, rrwf, KNOT_WIRE_MAX_PKTSIZE, + NULL, KNOT_RRSET_WIRE_CANONICAL); + if (written < 0) { free(rrwf); - return result; + return written; } - result = knot_dnssec_sign_add(ctx, rrwf, rr_wire_size); + int result = knot_dnssec_sign_add(ctx, rrwf, written); free(rrwf); return result; diff --git a/src/libknot/dnssec/sig0.c b/src/libknot/dnssec/sig0.c index fe6c516a901ff2f08b4b93704a7dadc790d167df..5a9342fc0f0dcbbc0488136707f5be2f40313048 100644 --- a/src/libknot/dnssec/sig0.c +++ b/src/libknot/dnssec/sig0.c @@ -146,17 +146,17 @@ int knot_sig0_sign(uint8_t *wire, size_t *wire_size, size_t wire_max_size, uint8_t *wire_end = wire + *wire_size; size_t wire_avail_size = wire_max_size - *wire_size; size_t wire_sig_size = 0; - uint16_t written_rr_count = 0; - int result = knot_rrset_to_wire(sig_rrset, wire_end, &wire_sig_size, - wire_avail_size, &written_rr_count, - NULL); + int result = knot_rrset_to_wire(sig_rrset, wire_end, wire_avail_size, + NULL, KNOT_RRSET_WIRE_CANONICAL); + knot_rrset_free(&sig_rrset, NULL); - if (result != KNOT_EOK) { + + if (result < 0) { return result; } - assert(written_rr_count == 1); + wire_sig_size = result; // create signature @@ -166,7 +166,7 @@ int knot_sig0_sign(uint8_t *wire, size_t *wire_size, size_t wire_max_size, } uint16_t wire_arcount = knot_wire_get_arcount(wire); - knot_wire_set_arcount(wire, wire_arcount + written_rr_count); + knot_wire_set_arcount(wire, wire_arcount + 1); *wire_size += wire_sig_size; diff --git a/src/libknot/errcode.c b/src/libknot/errcode.c index 63835f9a951dc8907ae341f28c7bbe47d23db369..cf2d809d3f9b8f4074a69404000172568abcab49 100644 --- a/src/libknot/errcode.c +++ b/src/libknot/errcode.c @@ -24,97 +24,98 @@ const error_table_t error_messages[] = { { KNOT_EOK, "OK" }, /* TSIG errors. */ - { KNOT_TSIG_EBADSIG, "Failed to verify TSIG MAC." }, - { KNOT_TSIG_EBADKEY, "TSIG key not recognized or invalid." }, - { KNOT_TSIG_EBADTIME, "TSIG signing time out of range." }, + { KNOT_TSIG_EBADSIG, "Failed to verify TSIG MAC" }, + { KNOT_TSIG_EBADKEY, "TSIG key not recognized or invalid" }, + { KNOT_TSIG_EBADTIME, "TSIG signing time out of range" }, /* Directly mapped error codes. */ - { KNOT_ENOMEM, "Not enough memory." }, - { KNOT_EINVAL, "Invalid parameter." }, - { KNOT_ENOTSUP, "Operation not supported." }, - { KNOT_EBUSY, "Requested resource is busy." }, - { KNOT_EAGAIN, "OS lacked necessary resources." }, - { KNOT_EACCES, "Operation not permitted." }, - { KNOT_ECONNREFUSED, "Connection refused." }, - { KNOT_EISCONN, "Already connected." }, - { KNOT_EADDRINUSE, "Address already in use." }, - { KNOT_ENOENT, "Not exists." }, - { KNOT_ERANGE, "Value is out of range." }, + { KNOT_ENOMEM, "Not enough memory" }, + { KNOT_EINVAL, "Invalid parameter" }, + { KNOT_ENOTSUP, "Operation not supported" }, + { KNOT_EBUSY, "Requested resource is busy" }, + { KNOT_EAGAIN, "OS lacked necessary resources" }, + { KNOT_EACCES, "Operation not permitted" }, + { KNOT_ECONNREFUSED, "Connection refused" }, + { KNOT_EISCONN, "Already connected" }, + { KNOT_EADDRINUSE, "Address already in use" }, + { KNOT_ENOENT, "Not exists" }, + { KNOT_ERANGE, "Value is out of range" }, /* General errors. */ - { KNOT_ERROR, "Failed." }, - { KNOT_ENOTRUNNING, "Resource is not running." }, - { KNOT_EPARSEFAIL, "Parser failed." }, - { KNOT_EEXPIRED, "Resource is expired." }, - { KNOT_EUPTODATE, "Zone is up-to-date." }, - { KNOT_EFEWDATA, "Not enough data to parse." }, - { KNOT_ESPACE, "Not enough space provided." }, - { KNOT_EMALF, "Malformed data." }, - { KNOT_ECRYPTO, "Error in crypto library." }, - { KNOT_ENSEC3PAR, "Missing or wrong NSEC3PARAM record." }, - { KNOT_ENSEC3CHAIN, "Missing or wrong NSEC3 chain in the zone." }, - { KNOT_EOUTOFZONE, "Name does not belong to the zone." }, - { KNOT_EHASH, "Error in hash table." }, - { KNOT_EZONEINVAL, "Invalid zone file." }, - { KNOT_EZONENOENT, "Zone file not found." }, - { KNOT_ENOZONE, "No such zone found." }, - { KNOT_ENONODE, "No such node in zone found." }, - { KNOT_EDNAMEPTR, "Domain name pointer larger than allowed." }, - { KNOT_EPAYLOAD, "Payload in OPT RR larger than max wire size." }, - { KNOT_ECRC, "CRC check failed." }, - { KNOT_EPREREQ, "UPDATE prerequisity not met." }, - { KNOT_ETTL, "TTL mismatch." }, - { KNOT_ENOXFR, "Transfer was not sent." }, - { KNOT_ENOIXFR, "Transfer is not IXFR (is in AXFR format)." }, - { KNOT_EXFRREFUSED, "Zone transfer refused by the server." }, - { KNOT_EDENIED, "Not allowed." }, - { KNOT_ECONN, "Connection reset." }, - { KNOT_ETIMEOUT, "Connection timeout." }, - { KNOT_EIXFRSPACE, "IXFR reply did not fit in." }, - { KNOT_ECNAME, "CNAME loop found in zone." }, - { KNOT_ENODIFF, "Cannot create zone diff." }, - { KNOT_EDSDIGESTLEN, "DS digest length does not match digest type." }, - { KNOT_ENOTSIG, "expected a TSIG or SIG(0)" }, - { KNOT_ELIMIT, "Exceeded response rate limit." }, - { KNOT_EWRITABLE, "File is not writable." }, - { KNOT_EOF, "End of file." }, + { KNOT_ERROR, "Failed" }, + { KNOT_ENOTRUNNING, "Resource is not running" }, + { KNOT_EPARSEFAIL, "Parser failed" }, + { KNOT_EEXPIRED, "Resource is expired" }, + { KNOT_EUPTODATE, "Zone is up-to-date" }, + { KNOT_EFEWDATA, "Not enough data to parse" }, + { KNOT_ESPACE, "Not enough space provided" }, + { KNOT_EMALF, "Malformed data" }, + { KNOT_ECRYPTO, "Error in crypto library" }, + { KNOT_ENSEC3PAR, "Missing or wrong NSEC3PARAM record" }, + { KNOT_ENSEC3CHAIN, "Missing or wrong NSEC3 chain in the zone" }, + { KNOT_EOUTOFZONE, "Name does not belong to the zone" }, + { KNOT_EHASH, "Error in hash table" }, + { KNOT_EZONEINVAL, "Invalid zone file" }, + { KNOT_EZONENOENT, "Zone file not found" }, + { KNOT_ENOZONE, "No such zone found" }, + { KNOT_ENONODE, "No such node in zone found" }, + { KNOT_EDNAMEPTR, "Domain name pointer larger than allowed" }, + { KNOT_EPAYLOAD, "Payload in OPT RR larger than max wire size" }, + { KNOT_ECRC, "CRC check failed" }, + { KNOT_EPREREQ, "UPDATE prerequisity not met" }, + { KNOT_ETTL, "TTL mismatch" }, + { KNOT_ENOXFR, "Transfer was not sent" }, + { KNOT_ENOIXFR, "Transfer is not IXFR (is in AXFR format)" }, + { KNOT_EXFRREFUSED, "Zone transfer refused by the server" }, + { KNOT_EDENIED, "Not allowed" }, + { KNOT_ECONN, "Connection reset" }, + { KNOT_ETIMEOUT, "Connection timeout" }, + { KNOT_EIXFRSPACE, "IXFR reply did not fit in" }, + { KNOT_ECNAME, "CNAME loop found in zone" }, + { KNOT_ENODIFF, "Cannot create zone diff" }, + { KNOT_EDSDIGESTLEN, "DS digest length does not match digest type" }, + { KNOT_ENOTSIG, "Expected a TSIG or SIG(0)" }, + { KNOT_ELIMIT, "Exceeded response rate limit" }, + { KNOT_EWRITABLE, "File is not writable" }, + { KNOT_EOF, "End of file" }, /* Control states. */ - { KNOT_CTL_STOP, "Stopping server." }, + { KNOT_CTL_STOP, "Stopping server" }, /* Network errors. */ - { KNOT_NET_EADDR, "Bad address or host name." }, - { KNOT_NET_ESOCKET, "Can't create socket." }, - { KNOT_NET_ECONNECT, "Can't connect." }, - { KNOT_NET_ESEND, "Can't send data." }, - { KNOT_NET_ERECV, "Can't receive data." }, - { KNOT_NET_ETIMEOUT, "Network timeout." }, + { KNOT_NET_EADDR, "Bad address or host name" }, + { KNOT_NET_ESOCKET, "Can't create socket" }, + { KNOT_NET_ECONNECT, "Can't connect" }, + { KNOT_NET_ESEND, "Can't send data" }, + { KNOT_NET_ERECV, "Can't receive data" }, + { KNOT_NET_ETIMEOUT, "Network timeout" }, /* Encoding errors. */ - { KNOT_BASE64_ESIZE, "Invalid base64 string length." }, - { KNOT_BASE64_ECHAR, "Invalid base64 character." }, - { KNOT_BASE32HEX_ESIZE, "Invalid base32hex string length." }, - { KNOT_BASE32HEX_ECHAR, "Invalid base32hex character." }, + { KNOT_BASE64_ESIZE, "Invalid base64 string length" }, + { KNOT_BASE64_ECHAR, "Invalid base64 character" }, + { KNOT_BASE32HEX_ESIZE, "Invalid base32hex string length" }, + { KNOT_BASE32HEX_ECHAR, "Invalid base32hex character" }, /* Key parsing errors. */ - { KNOT_KEY_EPUBLIC_KEY_OPEN, "Cannot open public key file." }, - { KNOT_KEY_EPRIVATE_KEY_OPEN, "Cannot open private key file." }, - { KNOT_KEY_EPUBLIC_KEY_INVALID, "Public key file is invalid." }, + { KNOT_KEY_EPUBLIC_KEY_OPEN, "Cannot open public key file" }, + { KNOT_KEY_EPRIVATE_KEY_OPEN, "Cannot open private key file" }, + { KNOT_KEY_EPUBLIC_KEY_INVALID, "Public key file is invalid" }, /* Key signing/verification errors. */ - { KNOT_DNSSEC_ENOTSUP, "Algorithm is not supported." }, - { KNOT_DNSSEC_EINVALID_KEY, "The signing key is invalid." }, - { KNOT_DNSSEC_EASSIGN_KEY, "Cannot assign the key." }, - { KNOT_DNSSEC_ECREATE_DIGEST_CONTEXT, "Cannot create digest context." }, - { KNOT_DNSSEC_EUNEXPECTED_SIGNATURE_SIZE, "Unexpected signature size." }, - { KNOT_DNSSEC_EDECODE_RAW_SIGNATURE, "Cannot decode the raw signature." }, - { KNOT_DNSSEC_EINVALID_SIGNATURE, "Signature is invalid." }, - { KNOT_DNSSEC_ESIGN, "Cannot create the signature." }, - { KNOT_DNSSEC_ENOKEY, "No keys for signing." }, - { KNOT_DNSSEC_ENOKEYDIR, "Keydir does not exist." }, + { KNOT_DNSSEC_ENOTSUP, "Algorithm is not supported" }, + { KNOT_DNSSEC_EINVALID_KEY, "The signing key is invalid" }, + { KNOT_DNSSEC_EASSIGN_KEY, "Cannot assign the key" }, + { KNOT_DNSSEC_ECREATE_DIGEST_CONTEXT, "Cannot create digest context" }, + { KNOT_DNSSEC_EUNEXPECTED_SIGNATURE_SIZE, "Unexpected signature size" }, + { KNOT_DNSSEC_EDECODE_RAW_SIGNATURE, "Cannot decode the raw signature" }, + { KNOT_DNSSEC_EINVALID_SIGNATURE, "Signature is invalid" }, + { KNOT_DNSSEC_ESIGN, "Cannot create the signature" }, + { KNOT_DNSSEC_ENOKEY, "No keys for signing" }, + { KNOT_DNSSEC_ENOKEYDIR, "Keydir does not exist" }, + { KNOT_DNSSEC_EMISSINGKEYTYPE, "Missing KSK or ZSK for used algorithm" }, /* NSEC3 errors. */ - { KNOT_NSEC3_ECOMPUTE_HASH, "Cannot compute NSEC3 hash." }, + { KNOT_NSEC3_ECOMPUTE_HASH, "Cannot compute NSEC3 hash" }, { KNOT_ERROR, NULL } /* Terminator */ }; diff --git a/src/libknot/errcode.h b/src/libknot/errcode.h index 3ce6aa286d1520eeea993540fcfaa4612981856a..9b64ea18e684274ebe30dc9cf3f8a77ee92f9232 100644 --- a/src/libknot/errcode.h +++ b/src/libknot/errcode.h @@ -37,66 +37,66 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. /*! \brief Error codes used in the library. */ enum knot_error { - KNOT_EOK = 0, /*!< OK */ + KNOT_EOK = 0, /* TSIG errors. */ - KNOT_TSIG_EBADSIG = -16, /*!< Failed to verify TSIG MAC. */ - KNOT_TSIG_EBADKEY = -17, /*!< TSIG key not recognized or invalid. */ - KNOT_TSIG_EBADTIME = -18, /*!< TSIG signing time out of range. */ + KNOT_TSIG_EBADSIG = -16, + KNOT_TSIG_EBADKEY = -17, + KNOT_TSIG_EBADTIME = -18, /* Directly mapped error codes. */ - KNOT_ENOMEM = knot_errno_to_error(ENOMEM), /*!< Out of memory. */ - KNOT_EINVAL = knot_errno_to_error(EINVAL), /*!< Invalid parameter passed. */ - KNOT_ENOTSUP = knot_errno_to_error(ENOTSUP), /*!< Parameter not supported. */ - KNOT_EBUSY = knot_errno_to_error(EBUSY), /*!< Requested resource is busy. */ - KNOT_EAGAIN = knot_errno_to_error(EAGAIN), /*!< OS lacked necessary resources. */ - KNOT_EACCES = knot_errno_to_error(EACCES), /*!< Permission is denied. */ - KNOT_ECONNREFUSED = knot_errno_to_error(ECONNREFUSED), /*!< Connection is refused. */ - KNOT_EISCONN = knot_errno_to_error(EISCONN), /*!< Already connected. */ - KNOT_EADDRINUSE = knot_errno_to_error(EADDRINUSE), /*!< Address already in use. */ - KNOT_ENOENT = knot_errno_to_error(ENOENT), /*!< Resource not found. */ - KNOT_ERANGE = knot_errno_to_error(ERANGE), /*!< Value is out of range. */ + KNOT_ENOMEM = knot_errno_to_error(ENOMEM), + KNOT_EINVAL = knot_errno_to_error(EINVAL), + KNOT_ENOTSUP = knot_errno_to_error(ENOTSUP), + KNOT_EBUSY = knot_errno_to_error(EBUSY), + KNOT_EAGAIN = knot_errno_to_error(EAGAIN), + KNOT_EACCES = knot_errno_to_error(EACCES), + KNOT_ECONNREFUSED = knot_errno_to_error(ECONNREFUSED), + KNOT_EISCONN = knot_errno_to_error(EISCONN), + KNOT_EADDRINUSE = knot_errno_to_error(EADDRINUSE), + KNOT_ENOENT = knot_errno_to_error(ENOENT), + KNOT_ERANGE = knot_errno_to_error(ERANGE), /* General errors. */ - KNOT_ERROR = -10000, /*!< Failed. */ - KNOT_ENOTRUNNING, /*!< Resource is not running. */ - KNOT_EPARSEFAIL, /*!< Parser fail. */ - KNOT_EEXPIRED, /*!< Resource is expired. */ - KNOT_EUPTODATE, /*!< Zone is up-to-date. */ - KNOT_EFEWDATA, /*!< Not enough data to parse. */ - KNOT_ESPACE, /*!< Not enough space provided. */ - KNOT_EMALF, /*!< Malformed data. */ - KNOT_ECRYPTO, /*!< Error in crypto library. */ - KNOT_ENSEC3PAR, /*!< Missing or wrong NSEC3PARAM record. */ - KNOT_ENSEC3CHAIN, /*!< Missing or wrong NSEC3 chain in the zone. */ - KNOT_EOUTOFZONE, /*!< Domain name does not belong to the zone. */ - KNOT_EHASH, /*!< Error in hash table. */ - KNOT_EZONEINVAL, /*!< Invalid zone file. */ - KNOT_EZONENOENT, /*!< Zone file not found. */ - KNOT_ENOZONE, /*!< No such zone found. */ - KNOT_ENONODE, /*!< No such node in zone found. */ - KNOT_EDNAMEPTR, /*!< Domain name pointer larger than allowed. */ - KNOT_EPAYLOAD, /*!< Payload in OPT RR larger than max wire size. */ - KNOT_ECRC, /*!< Wrong dump CRC. */ - KNOT_EPREREQ, /*!< UPDATE prerequisity not met. */ - KNOT_ETTL, /*!< TTL mismatch. */ - KNOT_ENOXFR, /*!< Transfer was not sent. */ - KNOT_ENOIXFR, /*!< Transfer is not IXFR (is in AXFR format). */ - KNOT_EXFRREFUSED, /*!< Zone transfer refused by the server. */ - KNOT_EDENIED, /*!< Not allowed. */ - KNOT_ECONN, /*!< Connection reset. */ - KNOT_ETIMEOUT, /*!< Connection timeout. */ - KNOT_EIXFRSPACE, /*!< IXFR reply did not fit in. */ - KNOT_ECNAME, /*!< CNAME loop found in zone. */ - KNOT_ENODIFF, /*!< No zone diff can be created. */ - KNOT_EDSDIGESTLEN, /*!< DS digest length does not match digest type. */ - KNOT_ENOTSIG, /*!< Expected a TSIG or SIG(0). */ - KNOT_ELIMIT, /*!< Exceeded response rate limit. */ - KNOT_EWRITABLE, /*!< File is not writable. */ - KNOT_EOF, /*!< End of file. */ + KNOT_ERROR = -10000, + KNOT_ENOTRUNNING, + KNOT_EPARSEFAIL, + KNOT_EEXPIRED, + KNOT_EUPTODATE, + KNOT_EFEWDATA, + KNOT_ESPACE, + KNOT_EMALF, + KNOT_ECRYPTO, + KNOT_ENSEC3PAR, + KNOT_ENSEC3CHAIN, + KNOT_EOUTOFZONE, + KNOT_EHASH, + KNOT_EZONEINVAL, + KNOT_EZONENOENT, + KNOT_ENOZONE, + KNOT_ENONODE, + KNOT_EDNAMEPTR, + KNOT_EPAYLOAD, + KNOT_ECRC, + KNOT_EPREREQ, + KNOT_ETTL, + KNOT_ENOXFR, + KNOT_ENOIXFR, + KNOT_EXFRREFUSED, + KNOT_EDENIED, + KNOT_ECONN, + KNOT_ETIMEOUT, + KNOT_EIXFRSPACE, + KNOT_ECNAME, + KNOT_ENODIFF, + KNOT_EDSDIGESTLEN, + KNOT_ENOTSIG, + KNOT_ELIMIT, + KNOT_EWRITABLE, + KNOT_EOF, /* Control states. */ - KNOT_CTL_STOP, /*!< Stop requested. */ + KNOT_CTL_STOP, /* Network errors. */ KNOT_NET_EADDR, @@ -128,6 +128,7 @@ enum knot_error { KNOT_DNSSEC_ESIGN, KNOT_DNSSEC_ENOKEY, KNOT_DNSSEC_ENOKEYDIR, + KNOT_DNSSEC_EMISSINGKEYTYPE, /* NSEC3 errors. */ KNOT_NSEC3_ECOMPUTE_HASH diff --git a/src/libknot/mempattern.c b/src/libknot/mempattern.c index 2d38e6051ca9501087f7702481cb202e9734828f..ebd3545a21b8d7f65c4917f692954cea1b223b3a 100644 --- a/src/libknot/mempattern.c +++ b/src/libknot/mempattern.c @@ -39,6 +39,26 @@ void *mm_alloc(mm_ctx_t *mm, size_t size) } } +void *mm_realloc(mm_ctx_t *mm, void *what, size_t size, size_t prev_size) +{ + if (mm) { + void *p = mm->alloc(mm->ctx, size); + if (p == NULL) { + return NULL; + } else { + if (what) { + memcpy(p, what, + prev_size < size ? prev_size : size); + } + mm_free(mm, what); + return p; + } + } else { + return realloc(what, size); + } +} + + void mm_free(mm_ctx_t *mm, void *what) { if (mm) { diff --git a/src/libknot/mempattern.h b/src/libknot/mempattern.h index 0cd7c14c23069e88aed92b7b7b1f60a39869868a..277eb45d884cd2b2d9a741f03a020f8e9570aeb8 100644 --- a/src/libknot/mempattern.h +++ b/src/libknot/mempattern.h @@ -46,6 +46,8 @@ typedef struct mm_ctx { /*! \brief Allocs using 'mm' if any, uses system malloc() otherwise. */ void *mm_alloc(mm_ctx_t *mm, size_t size); /*! \brief Reallocs using 'mm' if any, uses system realloc() otherwise. */ +void *mm_realloc(mm_ctx_t *mm, void *what, size_t size, size_t prev_size); +/*! \brief Free using 'mm' if any, uses system free() otherwise. */ void mm_free(mm_ctx_t *mm, void *what); /*! \brief Initialize default memory allocation context. */ diff --git a/src/libknot/packet/compr.c b/src/libknot/packet/compr.c index ffee3ed49400bbd034b8603bb265d6efc4593bde..c24f89f260cd5738c048bdda6242cfd5a4b361ed 100644 --- a/src/libknot/packet/compr.c +++ b/src/libknot/packet/compr.c @@ -125,13 +125,17 @@ int knot_compr_put_dname(const knot_dname_t *dname, uint8_t *dst, uint16_t max, written += sizeof(uint16_t); } + assert(dst >= compr->wire); + size_t wire_pos = dst - compr->wire; + assert(wire_pos < KNOT_WIRE_MAX_PKTSIZE); + /* Heuristics - expect similar names are grouped together. */ - if (written > sizeof(uint16_t) && compr->wire_pos + written < KNOT_WIRE_PTR_MAX) { - compr->suffix.pos = compr->wire_pos; + if (written > sizeof(uint16_t) && wire_pos + written < KNOT_WIRE_PTR_MAX) { + compr->suffix.pos = wire_pos; compr->suffix.labels = orig_labels; } dbg_packet("%s: compressed to %u bytes (match=%zu,@%zu)\n", - __func__, written, dname - match_begin, compr->wire_pos); + __func__, written, dname - match_begin, wire_pos); return written; } diff --git a/src/libknot/packet/compr.h b/src/libknot/packet/compr.h index 732f0ab00fcbae2dbae7a323097cc25791c8a88e..13a7162660fbe89e3e3d0d963ddfbe15399dc65a 100644 --- a/src/libknot/packet/compr.h +++ b/src/libknot/packet/compr.h @@ -69,7 +69,6 @@ typedef struct { */ typedef struct knot_compr { uint8_t *wire; /* Packet wireformat. */ - size_t wire_pos; /* Current wire position. */ knot_rrinfo_t *rrinfo; /* Hints for current RRSet. */ struct { uint16_t pos; /* Position of current suffix. */ diff --git a/src/libknot/packet/pkt.c b/src/libknot/packet/pkt.c index af8ce08187eadd0ce871522e8368faac1679efb6..d9350952eb9f8ecab6438ff8ec33a147c8c33e2c 100644 --- a/src/libknot/packet/pkt.c +++ b/src/libknot/packet/pkt.c @@ -489,22 +489,19 @@ int knot_pkt_put(knot_pkt_t *pkt, uint16_t compr_hint, const knot_rrset_t *rr, } uint8_t *pos = pkt->wire + pkt->size; - uint16_t rr_added = 0; size_t maxlen = pkt_remaining(pkt); - size_t len = maxlen; /* Create compression context. */ knot_compr_t compr; compr.wire = pkt->wire; - compr.wire_pos = pkt->size; compr.rrinfo = rrinfo; compr.suffix.pos = KNOT_WIRE_HEADER_SIZE; compr.suffix.labels = knot_dname_labels(compr.wire + compr.suffix.pos, compr.wire); /* Write RRSet to wireformat. */ - int ret = knot_rrset_to_wire(rr, pos, &len, maxlen, &rr_added, &compr); - if (ret != KNOT_EOK) { + int ret = knot_rrset_to_wire(rr, pos, maxlen, &compr, 0); + if (ret < 0) { dbg_packet("%s: rr_to_wire = %s\n,", __func__, knot_strerror(ret)); /* Truncate packet if required. */ @@ -515,6 +512,9 @@ int knot_pkt_put(knot_pkt_t *pkt, uint16_t compr_hint, const knot_rrset_t *rr, return ret; } + size_t len = ret; + uint16_t rr_added = rr->rrs.rr_count; + /* Keep reference to special types. */ if (rr->type == KNOT_RRTYPE_OPT) { pkt->opt_rr = &pkt->rr[pkt->rrset_count]; @@ -656,6 +656,49 @@ static int knot_pkt_rr_from_wire(const uint8_t *wire, size_t *pos, return KNOT_EOK; } +/* \note Private for check_rr_constraints(). */ +#define CHECK_AR_CONSTRAINTS(pkt, rr, var, check_func) \ + if ((pkt)->current != KNOT_ADDITIONAL) { \ + dbg_packet("%s: RRTYPE%u not in AR\n", __func__, rr->type); \ + return KNOT_EMALF; \ + } else if ((pkt)->var != NULL) { \ + dbg_packet("%s: found 2nd RRTYPE%u\n", __func__, rr->type); \ + return KNOT_EMALF; \ + } else if (!check_func(rr)) { \ + dbg_packet("%s: bad RRTYPE%u RDATA\n", __func__, rr->type); \ + return KNOT_EMALF; \ + } else { \ + (pkt)->var = rr; \ + } + +/*! \brief Check constraints (position, uniqueness, validity) for special types (TSIG, OPT). */ +static int check_rr_constraints(knot_pkt_t *pkt, knot_rrset_t *rr, size_t rr_size, unsigned flags) +{ + /* Check RR constraints. */ + switch(rr->type) { + case KNOT_RRTYPE_TSIG: + CHECK_AR_CONSTRAINTS(pkt, rr, tsig_rr, tsig_rdata_is_ok); + + /* Strip TSIG RR from wireformat and decrease ARCOUNT. */ + if (!(flags & KNOT_PF_KEEPWIRE)) { + pkt->parsed -= rr_size; + pkt->size -= rr_size; + knot_wire_set_id(pkt->wire, tsig_rdata_orig_id(rr)); + knot_wire_set_arcount(pkt->wire, knot_wire_get_arcount(pkt->wire) - 1); + } + break; + case KNOT_RRTYPE_OPT: + CHECK_AR_CONSTRAINTS(pkt, rr, opt_rr, knot_edns_check_record); + break; + default: + break; + } + + return KNOT_EOK; +} + +#undef CHECK_AR_RECORD + int knot_pkt_parse_rr(knot_pkt_t *pkt, unsigned flags) { dbg_packet("%s(%p, %u)\n", __func__, pkt, flags); @@ -689,53 +732,10 @@ int knot_pkt_parse_rr(knot_pkt_t *pkt, unsigned flags) /* Update packet RRSet count. */ ++pkt->rrset_count; - - /* Update section RRSet count. */ ++pkt->sections[pkt->current].count; - /* Check RR constraints. */ - switch(rr->type) { - case KNOT_RRTYPE_TSIG: - // if there is some TSIG already, treat as malformed - if (pkt->tsig_rr != NULL) { - dbg_packet("%s: found 2nd TSIG\n", __func__); - return KNOT_EMALF; - } else if (!tsig_rdata_is_ok(rr)) { - dbg_packet("%s: bad TSIG RDATA\n", __func__); - return KNOT_EMALF; - } - - /* Strip TSIG RR from wireformat and decrease ARCOUNT. */ - if (!(flags & KNOT_PF_KEEPWIRE)) { - pkt->parsed -= rr_size; - pkt->size -= rr_size; - knot_wire_set_id(pkt->wire, tsig_rdata_orig_id(rr)); - knot_wire_set_arcount(pkt->wire, knot_wire_get_arcount(pkt->wire) - 1); - } - - /* Remember TSIG RR. */ - pkt->tsig_rr = rr; - break; - case KNOT_RRTYPE_OPT: - /* If there is some OPT already, treat as malformed. */ - if (pkt->opt_rr != NULL) { - dbg_packet("%s: found 2nd OPT\n", __func__); - return KNOT_EMALF; - } - - /* Semantic checks for the OPT. */ - if (!knot_edns_check_record(rr)) { - dbg_packet("%s: OPT RR check failed\n", __func__); - return KNOT_EMALF; - } - - pkt->opt_rr = rr; - break; - default: - break; - } - - return ret; + /* Check special RRs (OPT and TSIG). */ + return check_rr_constraints(pkt, rr, rr_size, flags); } int knot_pkt_parse_section(knot_pkt_t *pkt, unsigned flags) diff --git a/src/libknot/processing/requestor.h b/src/libknot/processing/requestor.h index d833f9c19f9867cdc0c4e1a52f48ff751781244f..a2abef5612aa45fe73157b7836e69447b65b6e66 100644 --- a/src/libknot/processing/requestor.h +++ b/src/libknot/processing/requestor.h @@ -45,6 +45,7 @@ struct knot_request_data { unsigned flags; struct sockaddr_storage remote, origin; knot_pkt_t *query; + knot_pkt_t *resp; }; /*! diff --git a/src/libknot/rdataset.c b/src/libknot/rdataset.c index 233f0b7af19954714c7ef77a57248835e7a7fe73..847475558e030e9d6f2df68a38890e1e66d3e2cb 100644 --- a/src/libknot/rdataset.c +++ b/src/libknot/rdataset.c @@ -22,26 +22,6 @@ #include "libknot/rdataset.h" #include "libknot/common.h" -static void *mm_realloc(mm_ctx_t *mm, void *what, size_t size, size_t prev_size) -{ - if (mm) { - void *p = mm_alloc(mm, size); - if (knot_unlikely(p == NULL)) { - return NULL; - } else { - if (what) { - memcpy(p, what, - prev_size < size ? prev_size : size); - } - - mm_free(mm, what); - return p; - } - } else { - return realloc(what, size); - } -} - static knot_rdata_t *rr_seek(knot_rdata_t *d, size_t pos) { if (d == NULL) { diff --git a/src/libknot/rrset-dump.c b/src/libknot/rrset-dump.c index 9f6ee4c9308c1dc659082c5ce37b6f75cdba1aa6..1cae892b35113ddda70192b747b442cb81e13842 100644 --- a/src/libknot/rrset-dump.c +++ b/src/libknot/rrset-dump.c @@ -1981,7 +1981,7 @@ int knot_rrset_txt_dump(const knot_rrset_t *rrset, const size_t maxlen, const knot_dump_style_t *style) { - if (knot_rrset_empty(rrset) || dst == NULL || style == NULL) { + if (rrset == NULL || dst == NULL || style == NULL) { return KNOT_EINVAL; } @@ -1989,7 +1989,7 @@ int knot_rrset_txt_dump(const knot_rrset_t *rrset, int ret; // UPDATE delete may have empty RDATA => dump header. - if (rrset->rrs.rr_count == 0) { + if (knot_rrset_empty(rrset)) { // Dump rdata owner, class, ttl and type. ret = knot_rrset_txt_dump_header(rrset, 0, dst + len, maxlen - len, style); diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c index 1686ca4f10bf81bdcc53e6a2f03a16fedf0be7a4..ba6c44a5461a115baaeb57511f42a88471b43d0e 100644 --- a/src/libknot/rrset.c +++ b/src/libknot/rrset.c @@ -33,291 +33,389 @@ #include "libknot/packet/pkt.h" #include "libknot/dname.h" -static uint16_t rrset_rdata_naptr_bin_chunk_size(const knot_rrset_t *rrset, - size_t pos) +/*! + * \brief Get maximal size of a domain name in a wire with given capacity. + */ +#define dname_max(wire_capacity) MIN(wire_capacity, KNOT_DNAME_MAXLEN) + +/*! + * \brief Get compression pointer for a given hint. + */ +static uint16_t compr_get_ptr(knot_compr_t *compr, int hint) { - if (rrset == NULL || pos >= rrset->rrs.rr_count) { + if (compr == NULL) { return 0; } - size_t size = 0; - const knot_rdata_t *rr_data = knot_rdataset_at(&rrset->rrs, pos); - uint8_t *rdata = knot_rdata_data(rr_data); - assert(rdata); + return compr->rrinfo->compress_ptr[hint]; +} - /* Two shorts at the beginning. */ - size += 4; - /* 3 binary TXTs with length in the first byte. */ - for (int i = 0; i < 3; i++) { - size += *(rdata + size) + 1; +/*! + * \brief Set compression pointer for a given hint. + */ +static void compr_set_ptr(knot_compr_t *compr, int hint, + const uint8_t *written_at, uint16_t written_size) +{ + if (compr == NULL) { + return; } - /* - * Dname remaning, but we usually want to get to the DNAME, so - * there's no need to include it in the returned size. - */ + assert(written_at >= compr->wire); - return size; -} + uint16_t offset = written_at - compr->wire; -static size_t rrset_rdata_remainder_size(const knot_rrset_t *rrset, - uint16_t offset, size_t pos) -{ - const knot_rdata_t *rr_data = knot_rdataset_at(&rrset->rrs, pos); - return knot_rdata_rdlen(rr_data) - offset; + knot_pkt_compr_hint_set(compr->rrinfo, hint, offset, written_size); } -static int knot_rrset_header_to_wire(const knot_rrset_t *rrset, uint32_t ttl, - uint8_t **pos, size_t max_size, - knot_compr_t *compr, size_t *size) +/*! + * \brief Write RR owner to wire. + */ +static int write_owner(const knot_rrset_t *rrset, + uint8_t **wire, size_t *capacity, + knot_compr_t *compr, knot_rrset_wire_flags_t flags) { - // Common size of items: type, class and ttl. - const size_t type_cls_ttl_len = 2 * sizeof(uint16_t) + sizeof(uint32_t); - // Rdata length item size. - const size_t rrlen_len = sizeof(uint16_t); + assert(rrset); + assert(wire && *wire); + assert(capacity); - if (rrset->owner == NULL) { - return KNOT_EMALF; - } + uint16_t owner_pointer = compr_get_ptr(compr, COMPR_HINT_OWNER); - const uint8_t *owner = NULL; - uint8_t owner_len = 0; - uint16_t *rr_compress = NULL; - if (compr && compr->rrinfo->compress_ptr[0] > 0) { - rr_compress = compr->rrinfo->compress_ptr; - owner_len = sizeof(uint16_t); + /* Check size */ + + size_t owner_size = 0; + if (owner_pointer > 0) { + owner_size = sizeof(uint16_t); } else { - owner = rrset->owner; - owner_len = knot_dname_size(owner); + owner_size = knot_dname_size(rrset->owner); } - dbg_packet("%s: max size: %zu, compressed owner: %s, owner size: %u\n", - __func__, max_size, rr_compress ? "yes" : "no", owner_len); - - // Check wire space for header. - if (*size + owner_len + type_cls_ttl_len + rrlen_len > max_size) { - dbg_rrset_detail("Header does not fit into wire.\n"); + if (owner_size > *capacity) { return KNOT_ESPACE; } - if (rr_compress && rr_compress[COMPR_HINT_OWNER] != 0) { - /* Put compression pointer. */ - knot_wire_put_pointer(*pos, rr_compress[COMPR_HINT_OWNER]); - *pos += owner_len; + /* Write result */ + + if (owner_pointer > 0) { + knot_wire_put_pointer(*wire, owner_pointer); } else { - /* Write owner, type, class and ttl to wire. */ - int ret = knot_compr_put_dname(owner, *pos, KNOT_DNAME_MAXLEN, compr); - if (ret < 0) { - return ret; - } else { - owner_len = ret; - *pos += owner_len; + int written = knot_compr_put_dname(rrset->owner, *wire, + dname_max(*capacity), compr); + if (written < 0) { + return written; } - /* Store first dname compression hint. */ - if (compr) { - knot_pkt_compr_hint_set(compr->rrinfo, COMPR_HINT_OWNER, - compr->wire_pos, ret); + + if (flags & KNOT_RRSET_WIRE_CANONICAL) { + assert(compr == NULL); + knot_dname_to_lower(*wire); } + + compr_set_ptr(compr, COMPR_HINT_OWNER, *wire, written); + owner_size = written; + } + + /* Update buffer */ + + *wire += owner_size; + *capacity -= owner_size; + + return KNOT_EOK; +} + +/*! + * \brief Write RR type, class, and TTL to wire. + */ +static int write_fixed_header(const knot_rrset_t *rrset, uint16_t rrset_index, + uint8_t **wire, size_t *capacity) +{ + assert(rrset); + assert(rrset_index < rrset->rrs.rr_count); + assert(wire && *wire); + assert(capacity); + + /* Check capacity */ + + size_t size = sizeof(uint16_t) // type + + sizeof(uint16_t) // class + + sizeof(uint32_t); // ttl + + if (size > *capacity) { + return KNOT_ESPACE; } - dbg_rrset_detail(" Type: %u\n", rrset->type); - knot_wire_write_u16(*pos, rrset->type); - *pos += sizeof(uint16_t); + /* Write result */ + + uint32_t ttl = knot_rdata_ttl(knot_rdataset_at(&rrset->rrs, rrset_index)); + uint8_t *write = *wire; - dbg_rrset_detail(" Class: %u\n", rrset->rclass); - knot_wire_write_u16(*pos, rrset->rclass); - *pos += sizeof(uint16_t); + knot_wire_write_u16(write, rrset->type); + write += sizeof(uint16_t); + knot_wire_write_u16(write, rrset->rclass); + write += sizeof(uint16_t); + knot_wire_write_u32(write, ttl); + write += sizeof(uint32_t); + assert(write == *wire + size); - dbg_rrset_detail(" TTL: %u\n", ttl); - knot_wire_write_u32(*pos, ttl); - *pos += sizeof(uint32_t); + /* Update buffer */ - assert(owner_len != 0); - *size += owner_len + type_cls_ttl_len; + *wire = write; + *capacity -= size; return KNOT_EOK; } -/* [code-review] Split to more functions, this one's too long. */ -static int knot_rrset_rdata_to_wire_one(const knot_rrset_t *rrset, - uint16_t rdata_pos, uint8_t **pos, - size_t max_size, size_t *knot_rr_size, - knot_compr_t *compr) +/*! + * \brief Write RDATA DNAME to wire. + */ +static int write_rdata_dname(uint8_t **src, size_t *src_avail, + uint8_t **wire, size_t *capacity, + knot_compr_t *compr, int compr_hint, bool compress, + knot_rrset_wire_flags_t flags) { - assert(rrset); - assert(pos); + assert(src && *src); + assert(src_avail); + assert(wire && *wire); + assert(capacity); - /* Put RR header to wire. */ - size_t size = 0; - int ret = knot_rrset_header_to_wire(rrset, - knot_rdata_ttl(knot_rdataset_at(&rrset->rrs, rdata_pos)), - pos, max_size, - compr, &size); - if (ret != KNOT_EOK) { - dbg_response("Failed to convert RR header to wire (%s).\n", - knot_strerror(ret)); - return ret; + /* Source domain name */ + + const knot_dname_t *dname = *src; + size_t dname_size = knot_dname_size(dname); + + /* Output domain name */ + + int written = knot_compr_put_dname(dname, *wire, dname_max(*capacity), + compress ? compr : NULL); + if (written < 0) { + assert(written == KNOT_ESPACE); + return written; } - // save space for RDLENGTH - uint8_t *rdlength_pos = *pos; - *pos += 2; - size += 2; + /* Post-processing */ - if (compr) { - compr->wire_pos += size; + if (flags & KNOT_RRSET_WIRE_CANONICAL) { + assert(compr == NULL); + knot_dname_to_lower(*wire); } - /* Get pointer into RDATA array. */ - const knot_rdata_t *rr_data = knot_rdataset_at(&rrset->rrs, rdata_pos); - uint8_t *rdata = knot_rdata_data(rr_data); - assert(rdata); - /* Offset into one RDATA array. */ - size_t offset = 0; - /* Actual RDLENGTH. */ - uint16_t rdlength = 0; + /* Update compression hints */ - /* Compression pointer hint. */ - uint16_t hint_id = COMPR_HINT_RDATA + rdata_pos; + if (compr_get_ptr(compr, compr_hint) == 0) { + compr_set_ptr(compr, compr_hint, *wire, written); + } - const rdata_descriptor_t *desc = knot_get_rdata_descriptor(rrset->type); + /* Update buffers */ - for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) { - int item = desc->block_types[i]; - if (compr && knot_descriptor_item_is_compr_dname(item)) { - const knot_dname_t *dname = rdata + offset; - int ret = knot_compr_put_dname(dname, *pos, - max_size - size - rdlength, - compr); - if (ret < 0) { - return KNOT_ESPACE; - } - /* Store first dname compression hint. */ - if (!knot_pkt_compr_hint(compr->rrinfo, hint_id)) { - knot_pkt_compr_hint_set(compr->rrinfo, hint_id, compr->wire_pos, ret); - } - *pos += ret; - rdlength += ret; - offset += knot_dname_size(dname); - compr->wire_pos += ret; - } else if (knot_descriptor_item_is_dname(item)) { - const knot_dname_t *dname = rdata + offset; - // save whole domain name - size_t maxb = max_size - size - rdlength; - int dname_size = knot_dname_to_wire(*pos, dname, maxb); - if (dname_size < 0) - return KNOT_ESPACE; - /* Store first dname compression hint. */ - if (compr && !knot_pkt_compr_hint(compr->rrinfo, hint_id)) { - knot_pkt_compr_hint_set(compr->rrinfo, hint_id, compr->wire_pos, dname_size); - } - *pos += dname_size; - rdlength += dname_size; - offset += dname_size; - if (compr) { - compr->wire_pos += dname_size; - } - } else if (knot_descriptor_item_is_fixed(item)) { - /* Fixed length chunk. */ - if (size + rdlength + item > max_size) { - return KNOT_ESPACE; - } - memcpy(*pos, rdata + offset, item); - *pos += item; - rdlength += item; - offset += item; - if (compr) { - compr->wire_pos += item; - } - } else if (knot_descriptor_item_is_remainder(item)) { - /* Check that the remainder fits to stream. */ - size_t remainder_size = - rrset_rdata_remainder_size(rrset, offset, - rdata_pos); - if (size + rdlength + remainder_size > max_size) { - return KNOT_ESPACE; - } - memcpy(*pos, rdata + offset, remainder_size); - *pos += remainder_size; - rdlength += remainder_size; - offset += remainder_size; - if (compr) { - compr->wire_pos += remainder_size; - } - } else { - assert(rrset->type == KNOT_RRTYPE_NAPTR); - /* Store the binary chunk. */ - uint16_t chunk_size = - rrset_rdata_naptr_bin_chunk_size(rrset, rdata_pos); - if (size + rdlength + chunk_size > max_size) { - return KNOT_ESPACE; - } - memcpy(*pos, rdata + offset, chunk_size); - *pos += chunk_size; - rdlength += chunk_size; - offset += chunk_size; - if (compr) { - compr->wire_pos += chunk_size; - } - } + *wire += written; + *capacity -= written; + + *src += dname_size; + *src_avail -= dname_size; + + return KNOT_EOK; +} + +/*! + * \brief Write a fixed block of binary data to wire. + */ +static int write_rdata_fixed(uint8_t **src, size_t *src_avail, + uint8_t **wire, size_t *capacity, + size_t size) +{ + assert(src && *src); + assert(src_avail); + assert(wire && *wire); + assert(capacity); + + /* Check input/output buffer boundaries */ + + if (size > *src_avail) { + return KNOT_EMALF; + } + + if (size > *capacity) { + return KNOT_ESPACE; } - knot_wire_write_u16(rdlength_pos, rdlength); - size += rdlength; + /* Data binary copy */ + + memcpy(*wire, *src, size); + + /* Update buffers */ + + *src += size; + *src_avail -= size; + + *wire += size; + *capacity -= size; - *knot_rr_size = size; - assert(size <= max_size); return KNOT_EOK; } -static int knot_rrset_to_wire_aux(const knot_rrset_t *rrset, uint8_t **pos, - size_t max_size, knot_compr_t *comp) +/*! + * \brief Write RDATA NAPTR header to wire. + */ +static int write_rdata_naptr(uint8_t **src, size_t *src_avail, + uint8_t **wire, size_t *capacity) { + assert(src && *src); + assert(src_avail); + assert(wire && *wire); + assert(capacity); + size_t size = 0; - assert(rrset != NULL); - assert(rrset->owner != NULL); - assert(pos != NULL); - assert(*pos != NULL); - - // No RDATA, just save header and 0 RDLENGTH. - if (rrset->rrs.rr_count == 0) { - size_t header_size = 0; - int ret = knot_rrset_header_to_wire(rrset, 0, pos, max_size, comp, - &header_size); + + /* Fixed fields size (order, preference) */ + + size += 2 * sizeof(uint16_t); + + /* Variable fields size (flags, services, regexp) */ + + for (int i = 0; i < 3; i++) { + uint8_t *len_ptr = *src + size; + if (len_ptr >= *src + *src_avail) { + return KNOT_EMALF; + } + + size += 1 + *len_ptr; + } + + /* Copy result */ + + return write_rdata_fixed(src, src_avail, wire, capacity, size); +} + +/*! + * \brief Write one RDATA block to wire. + */ +static int write_rdata_block(uint8_t **src, size_t *src_avail, + uint8_t **wire, size_t *capacity, + int type, knot_compr_t *compr, int compr_hint_id, + knot_rrset_wire_flags_t flags) +{ + switch (type) { + case KNOT_RDATA_WF_COMPRESSED_DNAME: + return write_rdata_dname(src, src_avail, wire, capacity, + compr, compr_hint_id, true, flags); + case KNOT_RDATA_WF_UNCOMPRESSED_DNAME: + return write_rdata_dname(src, src_avail, wire, capacity, + compr, compr_hint_id, false, flags); + case KNOT_RDATA_WF_NAPTR_HEADER: + return write_rdata_naptr(src, src_avail, wire, capacity); + case KNOT_RDATA_WF_REMAINDER: + return write_rdata_fixed(src, src_avail, wire, capacity, *src_avail); + default: + /* Fixed size block */ + assert(type > 0); + return write_rdata_fixed(src, src_avail, wire, capacity, type); + } +} + +/*! + * \brief Write RDLENGTH and RDATA fields of a RR in a wire. + */ +static int write_rdata(const knot_rrset_t *rrset, uint16_t rrset_index, + uint8_t **wire, size_t *capacity, + knot_compr_t *compr, knot_rrset_wire_flags_t flags) +{ + assert(rrset); + assert(rrset_index < rrset->rrs.rr_count); + assert(wire && *wire); + assert(capacity); + + const knot_rdata_t *rdata = knot_rdataset_at(&rrset->rrs, rrset_index); + + /* Reserve space for RDLENGTH */ + + size_t rdlength_size = sizeof(uint16_t); + if (rdlength_size > *capacity) { + return KNOT_ESPACE; + } + + uint8_t *wire_rdlength = *wire; + *wire += rdlength_size; + *capacity -= rdlength_size; + + /* Write RDATA */ + + uint8_t *wire_rdata_begin = *wire; + int compr_hint = COMPR_HINT_RDATA + rrset_index; + + uint8_t *src = knot_rdata_data(rdata); + size_t src_avail = knot_rdata_rdlen(rdata); + + const rdata_descriptor_t *desc = knot_get_rdata_descriptor(rrset->type); + for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; i++) { + int type = desc->block_types[i]; + int ret = write_rdata_block(&src, &src_avail, wire, capacity, + type, compr, compr_hint, flags); if (ret != KNOT_EOK) { return ret; } + } - // Save zero rdata length. - knot_wire_write_u16(*pos, 0); - *pos += sizeof(uint16_t); - header_size += sizeof(uint16_t); + if (src_avail > 0) { + return KNOT_EMALF; + } + + /* Write final RDLENGTH */ - return header_size; + size_t rdlength = *wire - wire_rdata_begin; + knot_wire_write_u16(wire_rdlength, rdlength); + + return KNOT_EOK; +} + +/*! + * Write one RR from a RR Set to wire. + */ +static int write_rr(const knot_rrset_t *rrset, uint16_t rrset_index, + uint8_t **wire, size_t *capacity, knot_compr_t *compr, + knot_rrset_wire_flags_t flags) +{ + int ret; + + ret = write_owner(rrset, wire, capacity, compr, flags); + if (ret != KNOT_EOK) { + return ret; + } + + ret = write_fixed_header(rrset, rrset_index, wire, capacity); + if (ret != KNOT_EOK) { + return ret; + } + + ret = write_rdata(rrset, rrset_index, wire, capacity, compr, flags); + if (ret != KNOT_EOK) { + return ret; + } + + return KNOT_EOK; +} + +int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, uint16_t max_size, + knot_compr_t *compr, knot_rrset_wire_flags_t flags) +{ + if (!rrset || !wire) { + return KNOT_EINVAL; } - // Save rrset records. - for (uint16_t i = 0; i < rrset->rrs.rr_count; ++i) { - dbg_rrset_detail("rrset: to_wire: Current max_size=%zu\n", - max_size); - size_t knot_rr_size = 0; - int ret = knot_rrset_rdata_to_wire_one(rrset, i, pos, max_size, - &knot_rr_size, comp); + if (flags & KNOT_RRSET_WIRE_CANONICAL) { + compr = NULL; + } + + uint8_t *write = wire; + size_t capacity = max_size; + + for (uint16_t i = 0; i < rrset->rrs.rr_count; i++) { + int ret = write_rr(rrset, i, &write, &capacity, compr, flags); if (ret != KNOT_EOK) { - dbg_rrset("rrset: to_wire: Cannot convert RR. " - "Reason: %s.\n", knot_strerror(ret)); return ret; } - dbg_rrset_detail("Converted RR nr=%d, size=%zu\n", i, knot_rr_size); - /* Change size of whole RRSet. */ - size += knot_rr_size; - /* Change max size. */ - max_size -= knot_rr_size; } - dbg_rrset_detail("Max size: %zu, size: %zu\n", max_size, size); + size_t written = write - wire; - return size; + return written; } static int binary_store(uint8_t *rdata, size_t *offset, size_t packet_offset, @@ -419,36 +517,6 @@ void knot_rrset_clear(knot_rrset_t *rrset, mm_ctx_t *mm) } } -int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size, - size_t max_size, uint16_t *rr_count, knot_compr_t *compr) -{ - if (rrset == NULL || wire == NULL || size == NULL || rr_count == NULL) { - return KNOT_EINVAL; - } - - uint8_t *pos = wire; - - int ret = knot_rrset_to_wire_aux(rrset, &pos, max_size, compr); - if (ret < 0) { - // some RR didn't fit in, so no RRs should be used - dbg_rrset_verb("Some RR didn't fit in.\n"); - return KNOT_ESPACE; - } - - // Check if the whole RRSet fit into packet. - assert(ret <= max_size); - assert(pos - wire == ret); - - *size = ret; - - dbg_rrset_detail("Size after: %zu\n", *size); - - // If the rrset is empty set record counter to 1. - *rr_count = rrset->rrs.rr_count > 0 ? rrset->rrs.rr_count : 1; - - return KNOT_EOK; -} - int knot_rrset_rdata_from_wire_one(knot_rrset_t *rrset, const uint8_t *wire, size_t *pos, size_t total_size, uint32_t ttl, diff --git a/src/libknot/rrset.h b/src/libknot/rrset.h index 8ad91686a412d2ecc154a214edaf89bd31c712a3..6dbd3d6d39ad6f8ec79cbf636badb192ca1e4463 100644 --- a/src/libknot/rrset.h +++ b/src/libknot/rrset.h @@ -126,19 +126,31 @@ void knot_rrset_clear(knot_rrset_t *rrset, mm_ctx_t *mm); /* ---------- Wire conversions (legacy, to be done in knot_pkt_t) ----------- */ /*! - * \brief Converts RRSet structure to wireformat, compression included. + * \brief Flags controlling RR set from/to wire conversion. + */ +enum knot_rrset_wire_flags { + KNOT_RRSET_WIRE_NONE = 0, + KNOT_RRSET_WIRE_CANONICAL = 1 << 0, +}; + +typedef enum knot_rrset_wire_flags knot_rrset_wire_flags_t; + +/*! + * \brief Write RR Set content to a wire. + * + * Function accepts \ref KNOT_RRSET_WIRE_CANONICAL flag, which causes the + * output to be written in canonical representation. * * \param rrset RRSet to be converted. - * \param wire Destination wire. - * \param size Output size. - * \param max_size Wire size. - * \param rr_count Output RR count. - * \param compr Compression data. + * \param wire Output wire buffer. + * \param max_size Capacity of wire buffer. + * \param compr Compression context. + * \param flags Flags controlling the output. * - * \return KNOT_E* + * \return Output size, negative number on error (KNOT_E*). */ -int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size, - size_t max_size, uint16_t *rr_count, struct knot_compr *compr); +int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, uint16_t max_size, + struct knot_compr *compr, knot_rrset_wire_flags_t flags); /*! * \brief Creates one RR from wire, stores it into 'rrset' diff --git a/src/libknot/rrtype/opt.h b/src/libknot/rrtype/opt.h index 48cac122f336cb84c3a1cf1e00c92769cc185b44..816469e925bba9058ff47f4aab02d37f4a0d8dc3 100644 --- a/src/libknot/rrtype/opt.h +++ b/src/libknot/rrtype/opt.h @@ -56,9 +56,15 @@ enum knot_edns_const { /*! \brief NSID option code. */ KNOT_EDNS_OPTION_NSID = 3, /*! \brief EDNS client subnet option code. */ - KNOT_EDNS_OPTION_CLIENT_SUBNET = 8 + KNOT_EDNS_OPTION_CLIENT_SUBNET = 8, + /*! \brief Extended RCODE BADVERS. */ + KNOT_EDNS_RCODE_BADVERS = 16, }; +/* Helpers for splitting extended RCODE. */ +#define KNOT_EDNS_RCODE_HI(rc) (rc >> 4) +#define KNOT_EDNS_RCODE_LO(rc) (rc & 0x000f) + /*! * \brief EDNS DO flag. * @@ -67,10 +73,6 @@ enum knot_edns_const { */ static const uint16_t KNOT_EDNS_FLAG_DO = (uint16_t)1 << 15; -/*! \brief Extended RCODE BADVERS. */ -static const uint8_t KNOT_EDNS_RCODE_BADVERS = 16; - - /*----------------------------------------------------------------------------*/ /* EDNS OPT RR handling functions. */ /*----------------------------------------------------------------------------*/ diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c index 8500cc63a8469b5d3d8b16b1d09b0a5fe8e54f3d..60bb9e6281f1f5817d3e8749cf5c5231666788b4 100644 --- a/src/libknot/tsig-op.c +++ b/src/libknot/tsig-op.c @@ -505,23 +505,21 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, } /* Set the digest. */ - size_t tsig_wire_len = 0; - dbg_tsig("TSIG: msg_len=%zu, msg_max_len=%zu, tsig_max_len=%zu\n", - *msg_len, msg_max_len, tsig_wire_len); - uint16_t rr_count = 0; tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp); /* Write RRSet to wire */ + ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len, - &tsig_wire_len, msg_max_len - *msg_len, - &rr_count, NULL); - if (ret != KNOT_EOK) { + msg_max_len - *msg_len, NULL, 0); + if (ret < 0) { dbg_tsig("TSIG: rrset_to_wire = %s\n", knot_strerror(ret)); *digest_len = 0; knot_rrset_free(&tmp_tsig, NULL); return ret; } + size_t tsig_wire_len = ret; + knot_rrset_free(&tmp_tsig, NULL); dbg_tsig("TSIG: written TSIG RR (wire len %zu)\n", tsig_wire_len); @@ -619,22 +617,15 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len, dbg_tsig_verb("Message max length: %zu, message length: %zu\n", msg_max_len, *msg_len); - size_t tsig_wire_size = 0; - uint16_t rr_count = 0; ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len, - &tsig_wire_size, msg_max_len - *msg_len, - &rr_count, NULL); - if (ret != KNOT_EOK) { + msg_max_len - *msg_len, NULL, 0); + if (ret < 0) { knot_rrset_free(&tmp_tsig, NULL); *digest_len = 0; return ret; } - /* This should not happen, at least one rr has to be converted. */ - if (rr_count == 0) { - knot_rrset_free(&tmp_tsig, NULL); - return KNOT_EINVAL; - } + size_t tsig_wire_size = ret; knot_rrset_free(&tmp_tsig, NULL); @@ -823,7 +814,7 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len, tsig_rdata_set_other_data(tmp_tsig, 0, 0); /* Append TSIG RR. */ - int ret = knot_tsig_append(msg, msg_len, msg_max_len, tsig_rr); + int ret = knot_tsig_append(msg, msg_len, msg_max_len, tmp_tsig); /* key_name already referenced in RRSet, no need to free separately. */ knot_rrset_free(&tmp_tsig, NULL); @@ -834,20 +825,15 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len, int knot_tsig_append(uint8_t *msg, size_t *msg_len, size_t msg_max_len, const knot_rrset_t *tsig_rr) { - size_t tsig_wire_len = 0; - uint16_t rr_count = 0; - /* Write RRSet to wire */ - int ret = KNOT_ERROR; - ret = knot_rrset_to_wire(tsig_rr, msg + *msg_len, - &tsig_wire_len, msg_max_len - *msg_len, - &rr_count, NULL); - if (ret != KNOT_EOK) { + int ret = knot_rrset_to_wire(tsig_rr, msg + *msg_len, + msg_max_len - *msg_len, NULL, 0); + if (ret < 0) { dbg_tsig("TSIG: rrset_to_wire = %s\n", knot_strerror(ret)); return ret; } - *msg_len += tsig_wire_len; + *msg_len += ret; knot_wire_set_arcount(msg, knot_wire_get_arcount(msg) + 1); diff --git a/src/utils/nsupdate/nsupdate_exec.c b/src/utils/nsupdate/nsupdate_exec.c index ac096ee9237dacf1c3c3103cbded91183462544c..e9b5387835bbb6bb096de6284c47043a8898a50d 100644 --- a/src/utils/nsupdate/nsupdate_exec.c +++ b/src/utils/nsupdate/nsupdate_exec.c @@ -141,11 +141,7 @@ static int dname_isvalid(const char *lp, size_t len) { /* This is probably redundant, but should be a bit faster so let's keep it. */ static int parse_full_rr(zs_scanner_t *s, const char* lp) { - if (zs_scanner_process(lp, lp + strlen(lp), 0, s) < 0) { - return KNOT_EPARSEFAIL; - } - char nl = '\n'; /* Ensure newline after complete RR */ - if (zs_scanner_process(&nl, &nl+sizeof(char), 1, s) < 0) { /* Terminate */ + if (zs_scanner_parse(s, lp, lp + strlen(lp), true) < 0) { return KNOT_EPARSEFAIL; } @@ -164,9 +160,18 @@ static int parse_partial_rr(zs_scanner_t *s, const char *lp, unsigned flags) { int ret = KNOT_EOK; char b1[32], b2[32]; /* Should suffice for both class/type */ + bool fqdn = true; + /* Extract owner. */ size_t len = strcspn(lp, SEP_CHARS); char *owner_str = strndup(lp, len); + + /* Make dname FQDN if it isn't. */ + if (owner_str[len - 1] != '.') { + owner_str[len++] = '.'; + fqdn = false; + } + knot_dname_t *owner = knot_dname_from_str(owner_str); free(owner_str); if (owner == NULL) { @@ -175,6 +180,15 @@ static int parse_partial_rr(zs_scanner_t *s, const char *lp, unsigned flags) { s->r_owner_length = knot_dname_size(owner); memcpy(s->r_owner, owner, s->r_owner_length); + + /* Append origin if not FQDN. */ + if (!fqdn) { + s->r_owner_length--; + memcpy(s->r_owner + s->r_owner_length, s->zone_origin, + s->zone_origin_length); + s->r_owner_length += s->zone_origin_length; + } + lp = tok_skipspace(lp + len); /* Initialize */ @@ -246,7 +260,7 @@ static int parse_partial_rr(zs_scanner_t *s, const char *lp, unsigned flags) { /* Need to parse rdata, synthetize input. */ char *rr = sprintf_alloc("%s %u %s %s %s\n", owner_s, s->r_ttl, b1, b2, lp); - if (rr == NULL || zs_scanner_process(rr, rr + strlen(rr), 1, s) < 0) { + if (rr == NULL || zs_scanner_parse(s, rr, rr + strlen(rr), true) < 0) { ret = KNOT_EPARSEFAIL; } @@ -540,7 +554,7 @@ int cmd_del(const char* lp, nsupdate_params_t *params) /* Check owner name. */ if (rrp->r_owner_length == 0) { - ERR("failed to parse prereq owner name '%s'\n", lp); + ERR("failed to parse owner name '%s'\n", lp); return KNOT_EPARSEFAIL; } diff --git a/src/utils/nsupdate/nsupdate_params.c b/src/utils/nsupdate/nsupdate_params.c index e91f01e19e4313d4f83288b64b9d1f7691d240a0..1a0d4d2fbf5f5e9c31098d6a97dfbbc61dd4ff57 100644 --- a/src/utils/nsupdate/nsupdate_params.c +++ b/src/utils/nsupdate/nsupdate_params.c @@ -72,8 +72,8 @@ static int parser_set_default(zs_scanner_t *s, const char *fmt, ...) return KNOT_ESPACE; } - /* fmt must contain newline */ - if (zs_scanner_process(buf, buf + n, 1, s) < 0) { + /* Buffer must contain newline */ + if (zs_scanner_parse(s, buf, buf + n, true) < 0) { return KNOT_EPARSEFAIL; } @@ -108,7 +108,7 @@ static int nsupdate_init(nsupdate_params_t *params) params->zone = strdup("."); /* Initialize RR parser. */ - params->parser = zs_scanner_create(NULL, ".", params->class_num, 0, + params->parser = zs_scanner_create(".", params->class_num, 0, NULL, parse_err, NULL); if (!params->parser) return KNOT_ENOMEM; diff --git a/src/utils/nsupdate/nsupdate_params.h b/src/utils/nsupdate/nsupdate_params.h index 76600bac6655a66989117357b9fff4b87197a91a..ee4610f50db7ce40a6b4ba2b07dcf039abd9ff8f 100644 --- a/src/utils/nsupdate/nsupdate_params.h +++ b/src/utils/nsupdate/nsupdate_params.h @@ -30,7 +30,7 @@ #include "libknot/libknot.h" #include "common/lists.h" // list -#include "zscanner/zscanner.h" // zs_scanner_t +#include "zscanner/scanner.h" // zs_scanner_t #include "utils/common/netio.h" // server_t #include "utils/common/params.h" // protocol_t #include "libknot/dnssec/key.h" // knot_key_params_t diff --git a/src/zscanner/Makefile.am b/src/zscanner/Makefile.am index 65c9b9d7b5aa97efd5e3486decfccfb8df3d8d99..d93b5d5ac7e62f43962dbf84cef8910e292b366d 100644 --- a/src/zscanner/Makefile.am +++ b/src/zscanner/Makefile.am @@ -24,21 +24,18 @@ scanner.c: scanner.c.t0 endif libzscanner_la_SOURCES = \ - zscanner.h \ - scanner.h \ error.h \ error.c \ - loader.h \ - loader.c \ functions.h \ - functions.c + functions.c \ + scanner.h nodist_libzscanner_la_SOURCES = \ scanner.c libzscanner_la_CFLAGS = $(CODE_COVERAGE_CFLAGS) libzscanner_la_LDFLAGS = \ - $(CODE_COVERAGE_LDFLAGS) \ + $(CODE_COVERAGE_LDFLAGS) \ -version-info 0:1:0 \ -export-symbols-regex '^zs_' diff --git a/src/zscanner/error.c b/src/zscanner/error.c index 4e0115790a711be7b10dd3b69fd44a783ac3f3a4..f1f9a2e69789e41b049812820d1fc996140e2a32 100644 --- a/src/zscanner/error.c +++ b/src/zscanner/error.c @@ -29,137 +29,132 @@ typedef struct { const err_table_t err_msgs[] = { ERR_ITEM( ZS_OK, "OK" ), - /* Zone file loader errors. */ - ERR_ITEM( ZS_LOADER_FSTAT, - "Fstat error." ), - ERR_ITEM( ZS_LOADER_DIRECTORY, - "Zone file is a directory." ), - ERR_ITEM( ZS_LOADER_EMPTY, - "Empty zone file." ), - ERR_ITEM( ZS_LOADER_MMAP, - "Mmap error." ), - ERR_ITEM( ZS_LOADER_MUNMAP, - "Munmap error." ), - ERR_ITEM( ZS_LOADER_SCANNER, - "Zone processing error." ), - - /* Zone scanner errors. */ + ERR_ITEM( ZS_FILE_OPEN, + "File open error" ), + ERR_ITEM( ZS_FILE_PATH, + "File path error" ), + ERR_ITEM( ZS_FILE_FSTAT, + "Fstat error" ), + ERR_ITEM( ZS_FILE_DIRECTORY, + "Not zone file but directory" ), + ERR_ITEM( ZS_FILE_MMAP, + "File mmap error" ), ERR_ITEM( ZS_DOS_NEWLINE, - "Unsupported CRLF newline. Please, remove CR bytes." ), + "Unsupported CRLF newline. Please, remove CR bytes" ), ERR_ITEM( ZS_UNCOVERED_STATE, - "General scanner error." ), + "General scanner error" ), ERR_ITEM( ZS_UNCLOSED_MULTILINE, - "Unclosed last multiline block." ), + "Unclosed last multiline block" ), ERR_ITEM( ZS_LEFT_PARENTHESIS, - "Too many left parentheses." ), + "Too many left parentheses" ), ERR_ITEM( ZS_RIGHT_PARENTHESIS, - "Too many right parentheses." ), + "Too many right parentheses" ), ERR_ITEM( ZS_UNSUPPORTED_TYPE, - "Unsupported record type." ), + "Unsupported record type" ), ERR_ITEM( ZS_BAD_PREVIOUS_OWNER, - "Previous owner is invalid." ), + "Previous owner is invalid" ), ERR_ITEM( ZS_BAD_DNAME_CHAR, - "Invalid domain name character." ), + "Invalid domain name character" ), ERR_ITEM( ZS_BAD_OWNER, - "Owner is invalid." ), + "Owner is invalid" ), ERR_ITEM( ZS_LABEL_OVERFLOW, - "Maximal domain name label length has exceeded." ), + "Maximal domain name label length has exceeded" ), ERR_ITEM( ZS_DNAME_OVERFLOW, - "Maximal domain name length has exceeded." ), + "Maximal domain name length has exceeded" ), ERR_ITEM( ZS_BAD_NUMBER, - "Invalid number." ), + "Invalid number" ), ERR_ITEM( ZS_NUMBER64_OVERFLOW, - "Number is too big." ), + "Number is too big" ), ERR_ITEM( ZS_NUMBER32_OVERFLOW, - "Number is bigger than 32 bits." ), + "Number is bigger than 32 bits" ), ERR_ITEM( ZS_NUMBER16_OVERFLOW, - "Number is bigger than 16 bits." ), + "Number is bigger than 16 bits" ), ERR_ITEM( ZS_NUMBER8_OVERFLOW, - "Number is bigger than 8 bits." ), + "Number is bigger than 8 bits" ), ERR_ITEM( ZS_FLOAT_OVERFLOW, - "Float number overflow." ), + "Float number overflow" ), ERR_ITEM( ZS_RDATA_OVERFLOW, - "Maximal record data length has exceeded." ), + "Maximal record data length has exceeded" ), ERR_ITEM( ZS_ITEM_OVERFLOW, - "Maximal item length has exceeded." ), + "Maximal item length has exceeded" ), ERR_ITEM( ZS_BAD_ADDRESS_CHAR, - "Invalid address character." ), + "Invalid address character" ), ERR_ITEM( ZS_BAD_IPV4, - "Invalid IPv4 address." ), + "Invalid IPv4 address" ), ERR_ITEM( ZS_BAD_IPV6, - "Invalid IPv6 address." ), + "Invalid IPv6 address" ), ERR_ITEM( ZS_BAD_GATEWAY, - "Invalid gateway." ), + "Invalid gateway" ), ERR_ITEM( ZS_BAD_GATEWAY_KEY, - "Invalid gateway key." ), + "Invalid gateway key" ), ERR_ITEM( ZS_BAD_APL, - "Invalid address prefix list." ), + "Invalid address prefix list" ), ERR_ITEM( ZS_BAD_RDATA, - "Invalid record data." ), + "Invalid record data" ), ERR_ITEM( ZS_BAD_HEX_RDATA, - "Invalid record data in hex format." ), + "Invalid record data in hex format" ), ERR_ITEM( ZS_BAD_HEX_CHAR, - "Invalid hexadecimal character." ), + "Invalid hexadecimal character" ), ERR_ITEM( ZS_BAD_BASE64_CHAR, - "Invalid Base64 character." ), + "Invalid Base64 character" ), ERR_ITEM( ZS_BAD_BASE32HEX_CHAR, - "Invalid Base32hex character." ), + "Invalid Base32hex character" ), ERR_ITEM( ZS_BAD_REST, - "Unexpected data." ), + "Unexpected data" ), ERR_ITEM( ZS_BAD_TIMESTAMP_CHAR, - "Invalid timestamp character." ), + "Invalid timestamp character" ), ERR_ITEM( ZS_BAD_TIMESTAMP_LENGTH, - "Invalid timestamp length." ), + "Invalid timestamp length" ), ERR_ITEM( ZS_BAD_TIMESTAMP, - "Invalid timestamp." ), + "Invalid timestamp" ), ERR_ITEM( ZS_BAD_DATE, - "Invalid date." ), + "Invalid date" ), ERR_ITEM( ZS_BAD_TIME, - "Invalid time." ), + "Invalid time" ), ERR_ITEM( ZS_BAD_TIME_UNIT, - "Invalid time unit." ), + "Invalid time unit" ), ERR_ITEM( ZS_BAD_BITMAP, - "Invalid bitmap." ), + "Invalid bitmap" ), ERR_ITEM( ZS_TEXT_OVERFLOW, - "Text is too long." ), + "Text is too long" ), ERR_ITEM( ZS_BAD_TEXT_CHAR, - "Invalid text character." ), + "Invalid text character" ), ERR_ITEM( ZS_BAD_TEXT, - "Invalid text string." ), + "Invalid text string" ), ERR_ITEM( ZS_BAD_DIRECTIVE, - "Invalid directive." ), + "Invalid directive" ), ERR_ITEM( ZS_BAD_TTL, - "Invalid zone TTL." ), + "Invalid zone TTL" ), ERR_ITEM( ZS_BAD_ORIGIN, - "Invalid FQDN zone origin." ), + "Invalid FQDN zone origin" ), ERR_ITEM( ZS_BAD_INCLUDE_FILENAME, - "Invalid filename in include directive." ), + "Invalid filename in include directive" ), ERR_ITEM( ZS_BAD_INCLUDE_ORIGIN, - "Invalid origin in include directive." ), + "Invalid origin in include directive" ), ERR_ITEM( ZS_UNPROCESSED_INCLUDE, - "Include file processing error." ), + "Include file processing error" ), ERR_ITEM( ZS_UNOPENED_INCLUDE, - "Include file opening error." ), + "Include file opening error" ), ERR_ITEM( ZS_BAD_RDATA_LENGTH, - "The rdata length statement is incorrect." ), + "The rdata length statement is incorrect" ), ERR_ITEM( ZS_CANNOT_TEXT_DATA, - "Unable to process text form for this type." ), + "Unable to process text form for this type" ), ERR_ITEM( ZS_BAD_LOC_DATA, - "Invalid zone location data." ), + "Invalid zone location data" ), ERR_ITEM( ZS_UNKNOWN_BLOCK, - "Unknown rdata block." ), + "Unknown rdata block" ), ERR_ITEM( ZS_BAD_ALGORITHM, - "Invalid algorithm." ), + "Invalid algorithm" ), ERR_ITEM( ZS_BAD_CERT_TYPE, - "Invalid certificate type." ), + "Invalid certificate type" ), ERR_ITEM( ZS_BAD_EUI_LENGTH, - "Invalid EUI length." ), + "Invalid EUI length" ), ERR_ITEM( ZS_BAD_L64_LENGTH, - "Invalid 64-bit locator." ), + "Invalid 64-bit locator" ), ERR_ITEM( ZS_BAD_CHAR_COLON, - "Missing colon character." ), + "Missing colon character" ), ERR_ITEM( ZS_BAD_CHAR_DASH, - "Missing dash character." ), + "Missing dash character" ), ERR_ITEM( 0, NULL ) // Terminator }; diff --git a/src/zscanner/error.h b/src/zscanner/error.h index a5ae8cd9654650e97509ebe8900b41588d762e5f..74f98dcdf7866bbb28050ffd7f71ecde228a3a59 100644 --- a/src/zscanner/error.h +++ b/src/zscanner/error.h @@ -27,13 +27,11 @@ enum err_codes { ZS_OK = 0, - ZS_LOADER_FSTAT = -1000, - ZS_LOADER_DIRECTORY, - ZS_LOADER_EMPTY, - ZS_LOADER_MMAP, - ZS_LOADER_MUNMAP, - ZS_LOADER_SCANNER, - + ZS_FILE_OPEN = -1000, + ZS_FILE_PATH, + ZS_FILE_FSTAT, + ZS_FILE_DIRECTORY, + ZS_FILE_MMAP, ZS_DOS_NEWLINE, ZS_UNCOVERED_STATE, ZS_UNCLOSED_MULTILINE, diff --git a/src/zscanner/loader.c b/src/zscanner/loader.c deleted file mode 100644 index 3c9916c9ecd5fb0244cf78c92f3ece6277da8eaf..0000000000000000000000000000000000000000 --- a/src/zscanner/loader.c +++ /dev/null @@ -1,161 +0,0 @@ -/* 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 <inttypes.h> // PRIu64 -#include <unistd.h> // sysconf -#include <stdio.h> // sprintf -#include <stdlib.h> // free -#include <stdbool.h> // bool -#include <string.h> // strlen -#include <fcntl.h> // open -#include <sys/stat.h> // fstat -#include <sys/mman.h> // mmap - -#include "zscanner/loader.h" -#include "zscanner/error.h" - -/*! \brief Mmap block size in bytes. This value is then adjusted to the - * multiple of memory pages which fit in. - */ -#define BLOCK_SIZE 30000000 - -/*! \brief Last artificial block which ensures final newline character. */ -#define NEWLINE_BLOCK "\n" - -zs_loader_t* zs_loader_create(const char *file_name, - const char *origin, - const uint16_t rclass, - const uint32_t ttl, - void (*process_record)(zs_scanner_t *), - void (*process_error)(zs_scanner_t *), - void *data) -{ - // Creating zeroed structure. - zs_loader_t *fl = calloc(1, sizeof(zs_loader_t)); - if (fl == NULL) { - return NULL; - } - - // Copying file name. - fl->file_name = strdup(file_name); - - // Opening zone file. - fl->fd = open(fl->file_name, O_RDONLY); - if (fl->fd == -1) { - free(fl->file_name); - free(fl); - return NULL; - } - - // Creating zone scanner. - fl->scanner = zs_scanner_create(fl->file_name, origin, rclass, ttl, - process_record, process_error, data); - if (fl->scanner == NULL) { - close(fl->fd); - free(fl->file_name); - free(fl); - return NULL; - } - - return fl; -} - -void zs_loader_free(zs_loader_t *fl) -{ - close(fl->fd); - free(fl->file_name); - zs_scanner_free(fl->scanner); - free(fl); -} - -int zs_loader_process(zs_loader_t *fl) -{ - long page_size; - uint64_t n_blocks; - uint64_t block_id; - uint64_t default_block_size; - struct stat file_stat; - - // Getting OS page size. - page_size = sysconf(_SC_PAGESIZE); - - // Getting file information. - if (fstat(fl->fd, &file_stat) == -1) { - return ZS_LOADER_FSTAT; - } - - // Check for directory. - if (S_ISDIR(file_stat.st_mode)) { - return ZS_LOADER_DIRECTORY; - } - - // Check for empty file. - if (file_stat.st_size == 0) { - return ZS_LOADER_EMPTY; - } - - // Block size adjustment to multiple of page size. - default_block_size = (BLOCK_SIZE / page_size) * page_size; - - // Number of blocks which cover the whole file (ceiling operation). - n_blocks = 1 + ((file_stat.st_size - 1) / default_block_size); - - // Loop over zone file blocks. - for (block_id = 0; block_id < n_blocks; block_id++) { - // Current block start to scan. - uint64_t scanner_start = block_id * default_block_size; - // Current block size to scan. - uint64_t block_size = default_block_size; - // Last data block mark. - bool is_last_block = false; - // Mmapped data. - char *data; - - // The last block is probably shorter. - if (block_id == (n_blocks - 1)) { - block_size = file_stat.st_size - scanner_start; - is_last_block = true; - } - - // Zone file block mapping. - data = mmap(0, block_size, PROT_READ, MAP_SHARED, fl->fd, - scanner_start); - if (data == MAP_FAILED) { - return ZS_LOADER_MMAP; - } - - // Scan zone file. - zs_scanner_process(data, data + block_size, false, fl->scanner); - - // Process the last artificial block (newline char) if not fatal. - if (is_last_block == true && fl->scanner->stop == 0) { - zs_scanner_process(NEWLINE_BLOCK, NEWLINE_BLOCK + 1, - true, fl->scanner); - } - - // Zone file block unmapping. - if (munmap(data, block_size) == -1) { - return ZS_LOADER_MUNMAP; - } - } - - // Check for scanner return. - if (fl->scanner->error_counter > 0) { - return ZS_LOADER_SCANNER; - } - - return ZS_OK; -} diff --git a/src/zscanner/loader.h b/src/zscanner/loader.h deleted file mode 100644 index b2845571dccbb9cb5aaa282519d08d3c4765b2fd..0000000000000000000000000000000000000000 --- a/src/zscanner/loader.h +++ /dev/null @@ -1,89 +0,0 @@ -/* 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/>. - */ -/*! - * \file loader.h - * - * \author Daniel Salzman <daniel.salzman@nic.cz> - * - * \brief Zone file loader. - * - * \addtogroup zone_scanner - * @{ - */ - -#pragma once - -#include <stdint.h> - -#include "zscanner/scanner.h" - -/*! \brief Structure for zone file loader (each included file has one). */ -typedef struct { - /*!< File descriptor. */ - int fd; - /*!< Zone file name this loader belongs to. */ - char *file_name; - /*!< Zone scanner context stucture. */ - zs_scanner_t *scanner; -} zs_loader_t; - -/*! - * \brief Creates file loader structure. - * - * \param file_name Name of file to process. - * \param origin Initial zone origin. - * \param rclass Zone class value. - * \param ttl Initial ttl value. - * \param process_record Processing callback function. - * \param process_error Error callback function. - * \param data Arbitrary data useful in callback functions. - * - * \retval loader if success. - * \retval 0 if error. - */ -zs_loader_t* zs_loader_create(const char *file_name, - const char *origin, - const uint16_t rclass, - const uint32_t ttl, - void (*process_record)(zs_scanner_t *), - void (*process_error)(zs_scanner_t *), - void *data); - -/*! - * \brief Destroys file loader structure. - * - * \param loader File loader structure. - */ -void zs_loader_free(zs_loader_t *loader); - -/*! - * \brief Processes zone file. - * - * Launches zone file processing using zone scanner. For each correctly - * recognized record data process_record callback function is called. If any - * syntax error occures, then process_error callback function is called. - * - * \note Zone scanner error code and other information are stored in - * fl.scanner context. - * - * \param loader File loader structure. - * - * \retval ZSCANNER_OK if success. - * \retval error_code if error. - */ -int zs_loader_process(zs_loader_t *loader); - -/*! @} */ diff --git a/src/zscanner/scanner.c.g2 b/src/zscanner/scanner.c.g2 index 65769c8ae42e0f91c84d6d91aecd8e365b631647..96f2b85401fd8155acb96215b02af1954e09033a 100644 --- a/src/zscanner/scanner.c.g2 +++ b/src/zscanner/scanner.c.g2 @@ -19,21 +19,31 @@ #include <stdint.h> // uint32_t #include <stdlib.h> // calloc #include <stdio.h> // sprintf +#include <fcntl.h> // open #include <libgen.h> // dirname #include <stdbool.h> // bool -#include <math.h> // pow #include <string.h> // strdup +#include <math.h> // pow +#include <sys/mman.h> // mmap #include <sys/types.h> // (OpenBSD) #include <sys/socket.h> // AF_INET (BSD) +#include <sys/stat.h> // fstat #include <netinet/in.h> // in_addr (BSD) #include <arpa/inet.h> // inet_pton +#include <unistd.h> // sysconf #include "zscanner/scanner.h" -#include "zscanner/loader.h" // loader in includes -#include "zscanner/error.h" #include "zscanner/functions.h" #include "libknot/descriptor.h" +/*! \brief Mmap block size in bytes. This value is then adjusted to the + * multiple of memory pages which fit in. + */ +#define BLOCK_SIZE 30000000 + +/*! \brief Last artificial block which ensures final newline character. */ +#define NEWLINE_BLOCK "\n" + /*! \brief Shorthand for setting warning data. */ #define WARN(code) { s->error_code = code; } /*! \brief Shorthand for setting error data. */ @@ -90,8 +100,7 @@ static const int zone_scanner_error = 0; -zs_scanner_t* zs_scanner_create(const char *file_name, - const char *origin, +zs_scanner_t* zs_scanner_create(const char *origin, const uint16_t rclass, const uint32_t ttl, void (*process_record)(zs_scanner_t *), @@ -100,26 +109,13 @@ zs_scanner_t* zs_scanner_create(const char *file_name, { char settings[1024]; - zs_scanner_t *s = calloc(1, sizeof(zs_scanner_t)); - if (s == NULL) { + if (origin == NULL) { return NULL; } - if (file_name != NULL) { - // Get absolute path of the zone file. - if (realpath(file_name, (char*)(s->buffer)) != NULL) { - char *full_name = strdup((char*)(s->buffer)); - s->path = strdup(dirname(full_name)); - free(full_name); - } else { - free(s); - return NULL; - } - - s->file_name = strdup(file_name); - } else { - s->path = strdup("."); - s->file_name = strdup("<NULL>"); + zs_scanner_t *s = calloc(1, sizeof(zs_scanner_t)); + if (s == NULL) { + return NULL; } // Nonzero initial scanner state. @@ -139,7 +135,7 @@ zs_scanner_t* zs_scanner_create(const char *file_name, } int ret = snprintf(settings, sizeof(settings), format, origin); if (ret <= 0 || (size_t)ret >= sizeof(settings) || - zs_scanner_process(settings, settings + ret, true, s) != 0) { + zs_scanner_parse(s, settings, settings + ret, true) != 0) { zs_scanner_free(s); return NULL; } @@ -150,6 +146,7 @@ zs_scanner_t* zs_scanner_create(const char *file_name, s->process_record = process_record ? process_record : &noop; s->process_error = process_error ? process_error : &noop; s->data = data; + s->path = strdup("."); s->line_counter = 1; return s; @@ -158,16 +155,15 @@ zs_scanner_t* zs_scanner_create(const char *file_name, void zs_scanner_free(zs_scanner_t *s) { if (s != NULL) { - free(s->file_name); free(s->path); free(s); } } -int zs_scanner_process(const char *start, - const char *end, - const bool is_complete, - zs_scanner_t *s) +static void parse_block(zs_scanner_t *s, + const char *start, + const char *end, + const bool is_eof) { // Necessary scanner variables. const char *p = start; @@ -194,7 +190,7 @@ int zs_scanner_process(const char *start, memcpy(stack, s->stack, sizeof(stack)); // End of file check. - if (is_complete == true) { + if (is_eof) { eof = (char *)pe; } @@ -1374,7 +1370,7 @@ tr81: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } { @@ -11555,7 +11551,7 @@ tr93: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } goto st1057; @@ -11578,7 +11574,7 @@ tr657: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } goto st1057; @@ -17722,7 +17718,7 @@ tr129: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } goto st1058; @@ -17749,7 +17745,7 @@ tr123: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } goto st1058; @@ -18441,7 +18437,7 @@ tr706: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } goto st1059; @@ -18471,7 +18467,7 @@ tr701: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } goto st1059; @@ -18904,7 +18900,7 @@ tr670: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } goto st1061; @@ -18926,7 +18922,7 @@ tr695: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } goto st1061; @@ -18978,7 +18974,7 @@ tr3204: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } { @@ -19006,7 +19002,7 @@ tr3227: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } { @@ -20115,7 +20111,7 @@ tr90: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } { @@ -20289,7 +20285,7 @@ tr659: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } { @@ -20856,7 +20852,7 @@ tr661: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } goto st1063; @@ -20948,7 +20944,7 @@ tr666: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } goto st1063; @@ -22843,7 +22839,7 @@ tr680: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } goto st1064; @@ -22869,7 +22865,7 @@ tr712: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } goto st1064; @@ -24031,7 +24027,7 @@ tr719: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } goto st1065; @@ -24130,7 +24126,7 @@ tr714: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } goto st1065; @@ -28759,7 +28755,7 @@ tr776: // In case of serious error, stop scanner. if (s->stop == true) { - return -1; + return; } } { @@ -28787,7 +28783,7 @@ tr778: // In case of serious error, stop scanner. if (s->stop == true) { - return -1; + return; } } { @@ -31105,25 +31101,30 @@ tr902: sizeof(s->buffer)); } - // Create new file loader for included zone file. - zs_loader_t *fl = zs_loader_create((char*)(s->buffer), - text_origin, - s->default_class, - s->default_ttl, - s->process_record, - s->process_error, - s->data); - if (fl != NULL) { - // Process included zone file. - ret = zs_loader_process(fl); - zs_loader_free(fl); - + // Create new scanner for included zone file. + zs_scanner_t *ss = zs_scanner_create(text_origin, + s->default_class, + s->default_ttl, + s->process_record, + s->process_error, + s->data); + if (ss != NULL) { + // Parse included zone file. + ret = zs_scanner_parse_file(ss, (char*)(s->buffer)); if (ret != 0) { - ERR(ZS_UNPROCESSED_INCLUDE); + // File internal errors are handled by error callback. + if (ss->error_counter > 0) { + ERR(ZS_UNPROCESSED_INCLUDE); + // General include file error. + } else { + ERR(ss->error_code); + } + zs_scanner_free(ss); p--; {goto st246;} } + zs_scanner_free(ss); } else { - ERR(ZS_UNOPENED_INCLUDE); + ERR(ZS_UNPROCESSED_INCLUDE); p--; {goto st246;} } } @@ -31158,25 +31159,30 @@ tr912: sizeof(s->buffer)); } - // Create new file loader for included zone file. - zs_loader_t *fl = zs_loader_create((char*)(s->buffer), - text_origin, - s->default_class, - s->default_ttl, - s->process_record, - s->process_error, - s->data); - if (fl != NULL) { - // Process included zone file. - ret = zs_loader_process(fl); - zs_loader_free(fl); - + // Create new scanner for included zone file. + zs_scanner_t *ss = zs_scanner_create(text_origin, + s->default_class, + s->default_ttl, + s->process_record, + s->process_error, + s->data); + if (ss != NULL) { + // Parse included zone file. + ret = zs_scanner_parse_file(ss, (char*)(s->buffer)); if (ret != 0) { - ERR(ZS_UNPROCESSED_INCLUDE); + // File internal errors are handled by error callback. + if (ss->error_counter > 0) { + ERR(ZS_UNPROCESSED_INCLUDE); + // General include file error. + } else { + ERR(ss->error_code); + } + zs_scanner_free(ss); p--; {goto st246;} } + zs_scanner_free(ss); } else { - ERR(ZS_UNOPENED_INCLUDE); + ERR(ZS_UNPROCESSED_INCLUDE); p--; {goto st246;} } } @@ -31217,25 +31223,30 @@ tr924: sizeof(s->buffer)); } - // Create new file loader for included zone file. - zs_loader_t *fl = zs_loader_create((char*)(s->buffer), - text_origin, - s->default_class, - s->default_ttl, - s->process_record, - s->process_error, - s->data); - if (fl != NULL) { - // Process included zone file. - ret = zs_loader_process(fl); - zs_loader_free(fl); - + // Create new scanner for included zone file. + zs_scanner_t *ss = zs_scanner_create(text_origin, + s->default_class, + s->default_ttl, + s->process_record, + s->process_error, + s->data); + if (ss != NULL) { + // Parse included zone file. + ret = zs_scanner_parse_file(ss, (char*)(s->buffer)); if (ret != 0) { - ERR(ZS_UNPROCESSED_INCLUDE); + // File internal errors are handled by error callback. + if (ss->error_counter > 0) { + ERR(ZS_UNPROCESSED_INCLUDE); + // General include file error. + } else { + ERR(ss->error_code); + } + zs_scanner_free(ss); p--; {goto st246;} } + zs_scanner_free(ss); } else { - ERR(ZS_UNOPENED_INCLUDE); + ERR(ZS_UNPROCESSED_INCLUDE); p--; {goto st246;} } } @@ -31486,25 +31497,30 @@ tr936: sizeof(s->buffer)); } - // Create new file loader for included zone file. - zs_loader_t *fl = zs_loader_create((char*)(s->buffer), - text_origin, - s->default_class, - s->default_ttl, - s->process_record, - s->process_error, - s->data); - if (fl != NULL) { - // Process included zone file. - ret = zs_loader_process(fl); - zs_loader_free(fl); - + // Create new scanner for included zone file. + zs_scanner_t *ss = zs_scanner_create(text_origin, + s->default_class, + s->default_ttl, + s->process_record, + s->process_error, + s->data); + if (ss != NULL) { + // Parse included zone file. + ret = zs_scanner_parse_file(ss, (char*)(s->buffer)); if (ret != 0) { - ERR(ZS_UNPROCESSED_INCLUDE); + // File internal errors are handled by error callback. + if (ss->error_counter > 0) { + ERR(ZS_UNPROCESSED_INCLUDE); + // General include file error. + } else { + ERR(ss->error_code); + } + zs_scanner_free(ss); p--; {goto st246;} } + zs_scanner_free(ss); } else { - ERR(ZS_UNOPENED_INCLUDE); + ERR(ZS_UNPROCESSED_INCLUDE); p--; {goto st246;} } } @@ -73036,11 +73052,11 @@ case 1140: // Processing error. s->process_error(s); - return -1; + return; } // Check unclosed multiline record. - if (is_complete && s->multiline) { + if (is_eof && s->multiline) { ERR(ZS_UNCLOSED_MULTILINE); s->error_counter++; s->process_error(s); @@ -73053,6 +73069,24 @@ case 1140: // Storing r_data pointer. s->r_data_tail = rdata_tail - s->r_data; +} + +int zs_scanner_parse(zs_scanner_t *s, + const char *start, + const char *end, + const bool final_block) +{ + if (s == NULL || start == NULL || end == NULL) { + return -1; + } + + // Parse input block. + parse_block(s, start, end, false); + + // Parse trailing artificial block (newline char) if not stop. + if (final_block && !s->stop) { + parse_block(s, NEWLINE_BLOCK, NEWLINE_BLOCK + 1, true); + } // Check if any errors has occured. if (s->error_counter > 0) { @@ -73061,3 +73095,120 @@ case 1140: return 0; } + +int zs_scanner_parse_file(zs_scanner_t *s, + const char *file_name) +{ + long page_size; + uint64_t n_blocks; + uint64_t block_id; + uint64_t default_block_size; + struct stat file_stat; + + if (s == NULL || file_name == NULL) { + return -1; + } + + // Copying file name. + s->file.name = strdup(file_name); + + // Opening the zone file. + s->file.descriptor = open(file_name, O_RDONLY); + if (s->file.descriptor == -1) { + ERR(ZS_FILE_OPEN); + free(s->file.name); + return -1; + } + + // Get absolute path of the zone file. + if (realpath(file_name, (char*)(s->buffer)) != NULL) { + char *full_name = strdup((char*)(s->buffer)); + free(s->path); + s->path = strdup(dirname(full_name)); + free(full_name); + } else { + ERR(ZS_FILE_PATH); + close(s->file.descriptor); + free(s->file.name); + return -1; + } + + // Getting OS page size. + page_size = sysconf(_SC_PAGESIZE); + + // Getting file information. + if (fstat(s->file.descriptor, &file_stat) == -1) { + ERR(ZS_FILE_FSTAT); + close(s->file.descriptor); + free(s->file.name); + return -1; + } + + // Check for directory. + if (S_ISDIR(file_stat.st_mode)) { + ERR(ZS_FILE_DIRECTORY); + close(s->file.descriptor); + free(s->file.name); + return -1; + } + + // Block size adjustment to multiple of page size. + default_block_size = (BLOCK_SIZE / page_size) * page_size; + + // Number of blocks which cover the whole file (ceiling operation). + n_blocks = 1 + ((file_stat.st_size - 1) / default_block_size); + + // Loop over zone file blocks. + for (block_id = 0; block_id < n_blocks; block_id++) { + // Current block start to scan. + uint64_t scanner_start = block_id * default_block_size; + // Current block size to scan. + uint64_t block_size = default_block_size; + // Last data block mark. + bool final_block = false; + // Mmapped data. + char *data; + + // The last block is probably shorter. + if (block_id == (n_blocks - 1)) { + block_size = file_stat.st_size - scanner_start; + final_block = true; + } + + // Zone file block mapping. + data = mmap(0, block_size, PROT_READ, MAP_SHARED, + s->file.descriptor, scanner_start); + if (data == MAP_FAILED) { + ERR(ZS_FILE_MMAP); + close(s->file.descriptor); + free(s->file.name); + return -1; + } + + // Scan zone file block. + zs_scanner_parse(s, data, data + block_size, final_block); + + // Zone file block unmapping. + if (munmap(data, block_size) == -1) { + ERR(ZS_FILE_MMAP); + close(s->file.descriptor); + free(s->file.name); + return -1; + } + + // Stop parsing if required. + if (s->stop) { + break; + } + } + + close(s->file.descriptor); + free(s->file.name); + + // Check for scanner return. + if (s->error_counter > 0) { + return -1; + } + + return 0; +} diff --git a/src/zscanner/scanner.c.t0 b/src/zscanner/scanner.c.t0 index 76bd22c0aa5d7219ae86b45b2b391a5fa9b86c5d..44af7e21f9d14f5fd26577bd861a7b44430482ab 100644 --- a/src/zscanner/scanner.c.t0 +++ b/src/zscanner/scanner.c.t0 @@ -19,21 +19,31 @@ #include <stdint.h> // uint32_t #include <stdlib.h> // calloc #include <stdio.h> // sprintf +#include <fcntl.h> // open #include <libgen.h> // dirname #include <stdbool.h> // bool -#include <math.h> // pow #include <string.h> // strdup +#include <math.h> // pow +#include <sys/mman.h> // mmap #include <sys/types.h> // (OpenBSD) #include <sys/socket.h> // AF_INET (BSD) +#include <sys/stat.h> // fstat #include <netinet/in.h> // in_addr (BSD) #include <arpa/inet.h> // inet_pton +#include <unistd.h> // sysconf #include "zscanner/scanner.h" -#include "zscanner/loader.h" // loader in includes -#include "zscanner/error.h" #include "zscanner/functions.h" #include "libknot/descriptor.h" +/*! \brief Mmap block size in bytes. This value is then adjusted to the + * multiple of memory pages which fit in. + */ +#define BLOCK_SIZE 30000000 + +/*! \brief Last artificial block which ensures final newline character. */ +#define NEWLINE_BLOCK "\n" + /*! \brief Shorthand for setting warning data. */ #define WARN(code) { s->error_code = code; } /*! \brief Shorthand for setting error data. */ @@ -4881,8 +4891,7 @@ static const int zone_scanner_error = 0; -zs_scanner_t* zs_scanner_create(const char *file_name, - const char *origin, +zs_scanner_t* zs_scanner_create(const char *origin, const uint16_t rclass, const uint32_t ttl, void (*process_record)(zs_scanner_t *), @@ -4891,26 +4900,13 @@ zs_scanner_t* zs_scanner_create(const char *file_name, { char settings[1024]; - zs_scanner_t *s = calloc(1, sizeof(zs_scanner_t)); - if (s == NULL) { + if (origin == NULL) { return NULL; } - if (file_name != NULL) { - // Get absolute path of the zone file. - if (realpath(file_name, (char*)(s->buffer)) != NULL) { - char *full_name = strdup((char*)(s->buffer)); - s->path = strdup(dirname(full_name)); - free(full_name); - } else { - free(s); - return NULL; - } - - s->file_name = strdup(file_name); - } else { - s->path = strdup("."); - s->file_name = strdup("<NULL>"); + zs_scanner_t *s = calloc(1, sizeof(zs_scanner_t)); + if (s == NULL) { + return NULL; } // Nonzero initial scanner state. @@ -4930,7 +4926,7 @@ zs_scanner_t* zs_scanner_create(const char *file_name, } int ret = snprintf(settings, sizeof(settings), format, origin); if (ret <= 0 || (size_t)ret >= sizeof(settings) || - zs_scanner_process(settings, settings + ret, true, s) != 0) { + zs_scanner_parse(s, settings, settings + ret, true) != 0) { zs_scanner_free(s); return NULL; } @@ -4941,6 +4937,7 @@ zs_scanner_t* zs_scanner_create(const char *file_name, s->process_record = process_record ? process_record : &noop; s->process_error = process_error ? process_error : &noop; s->data = data; + s->path = strdup("."); s->line_counter = 1; return s; @@ -4949,16 +4946,15 @@ zs_scanner_t* zs_scanner_create(const char *file_name, void zs_scanner_free(zs_scanner_t *s) { if (s != NULL) { - free(s->file_name); free(s->path); free(s); } } -int zs_scanner_process(const char *start, - const char *end, - const bool is_complete, - zs_scanner_t *s) +static void parse_block(zs_scanner_t *s, + const char *start, + const char *end, + const bool is_eof) { // Necessary scanner variables. const char *p = start; @@ -4985,7 +4981,7 @@ int zs_scanner_process(const char *start, memcpy(stack, s->stack, sizeof(stack)); // End of file check. - if (is_complete == true) { + if (is_eof) { eof = (char *)pe; } @@ -5253,7 +5249,7 @@ _match: // In case of serious error, stop scanner. if (s->stop == true) { - return -1; + return; } } break; @@ -5814,25 +5810,30 @@ _match: sizeof(s->buffer)); } - // Create new file loader for included zone file. - zs_loader_t *fl = zs_loader_create((char*)(s->buffer), - text_origin, - s->default_class, - s->default_ttl, - s->process_record, - s->process_error, - s->data); - if (fl != NULL) { - // Process included zone file. - ret = zs_loader_process(fl); - zs_loader_free(fl); - + // Create new scanner for included zone file. + zs_scanner_t *ss = zs_scanner_create(text_origin, + s->default_class, + s->default_ttl, + s->process_record, + s->process_error, + s->data); + if (ss != NULL) { + // Parse included zone file. + ret = zs_scanner_parse_file(ss, (char*)(s->buffer)); if (ret != 0) { - ERR(ZS_UNPROCESSED_INCLUDE); + // File internal errors are handled by error callback. + if (ss->error_counter > 0) { + ERR(ZS_UNPROCESSED_INCLUDE); + // General include file error. + } else { + ERR(ss->error_code); + } + zs_scanner_free(ss); p--; {cs = 246; goto _again;} } + zs_scanner_free(ss); } else { - ERR(ZS_UNOPENED_INCLUDE); + ERR(ZS_UNPROCESSED_INCLUDE); p--; {cs = 246; goto _again;} } } @@ -7117,7 +7118,7 @@ _match: // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } break; @@ -7372,11 +7373,11 @@ _again: // Processing error. s->process_error(s); - return -1; + return; } // Check unclosed multiline record. - if (is_complete && s->multiline) { + if (is_eof && s->multiline) { ERR(ZS_UNCLOSED_MULTILINE); s->error_counter++; s->process_error(s); @@ -7389,6 +7390,24 @@ _again: // Storing r_data pointer. s->r_data_tail = rdata_tail - s->r_data; +} + +int zs_scanner_parse(zs_scanner_t *s, + const char *start, + const char *end, + const bool final_block) +{ + if (s == NULL || start == NULL || end == NULL) { + return -1; + } + + // Parse input block. + parse_block(s, start, end, false); + + // Parse trailing artificial block (newline char) if not stop. + if (final_block && !s->stop) { + parse_block(s, NEWLINE_BLOCK, NEWLINE_BLOCK + 1, true); + } // Check if any errors has occured. if (s->error_counter > 0) { @@ -7397,3 +7416,120 @@ _again: return 0; } + +int zs_scanner_parse_file(zs_scanner_t *s, + const char *file_name) +{ + long page_size; + uint64_t n_blocks; + uint64_t block_id; + uint64_t default_block_size; + struct stat file_stat; + + if (s == NULL || file_name == NULL) { + return -1; + } + + // Copying file name. + s->file.name = strdup(file_name); + + // Opening the zone file. + s->file.descriptor = open(file_name, O_RDONLY); + if (s->file.descriptor == -1) { + ERR(ZS_FILE_OPEN); + free(s->file.name); + return -1; + } + + // Get absolute path of the zone file. + if (realpath(file_name, (char*)(s->buffer)) != NULL) { + char *full_name = strdup((char*)(s->buffer)); + free(s->path); + s->path = strdup(dirname(full_name)); + free(full_name); + } else { + ERR(ZS_FILE_PATH); + close(s->file.descriptor); + free(s->file.name); + return -1; + } + + // Getting OS page size. + page_size = sysconf(_SC_PAGESIZE); + + // Getting file information. + if (fstat(s->file.descriptor, &file_stat) == -1) { + ERR(ZS_FILE_FSTAT); + close(s->file.descriptor); + free(s->file.name); + return -1; + } + + // Check for directory. + if (S_ISDIR(file_stat.st_mode)) { + ERR(ZS_FILE_DIRECTORY); + close(s->file.descriptor); + free(s->file.name); + return -1; + } + + // Block size adjustment to multiple of page size. + default_block_size = (BLOCK_SIZE / page_size) * page_size; + + // Number of blocks which cover the whole file (ceiling operation). + n_blocks = 1 + ((file_stat.st_size - 1) / default_block_size); + + // Loop over zone file blocks. + for (block_id = 0; block_id < n_blocks; block_id++) { + // Current block start to scan. + uint64_t scanner_start = block_id * default_block_size; + // Current block size to scan. + uint64_t block_size = default_block_size; + // Last data block mark. + bool final_block = false; + // Mmapped data. + char *data; + + // The last block is probably shorter. + if (block_id == (n_blocks - 1)) { + block_size = file_stat.st_size - scanner_start; + final_block = true; + } + + // Zone file block mapping. + data = mmap(0, block_size, PROT_READ, MAP_SHARED, + s->file.descriptor, scanner_start); + if (data == MAP_FAILED) { + ERR(ZS_FILE_MMAP); + close(s->file.descriptor); + free(s->file.name); + return -1; + } + + // Scan zone file block. + zs_scanner_parse(s, data, data + block_size, final_block); + + // Zone file block unmapping. + if (munmap(data, block_size) == -1) { + ERR(ZS_FILE_MMAP); + close(s->file.descriptor); + free(s->file.name); + return -1; + } + + // Stop parsing if required. + if (s->stop) { + break; + } + } + + close(s->file.descriptor); + free(s->file.name); + + // Check for scanner return. + if (s->error_counter > 0) { + return -1; + } + + return 0; +} diff --git a/src/zscanner/scanner.h b/src/zscanner/scanner.h index 7b1fbd8f6e29993a79a74ff1ba7f2494ee0e7c17..d195d8e095bb08a5c18233c18d7fec1f4986015d 100644 --- a/src/zscanner/scanner.h +++ b/src/zscanner/scanner.h @@ -29,6 +29,8 @@ #include <stdint.h> #include <stdbool.h> +#include "zscanner/error.h" + /*! \brief Maximal length of rdata. */ #define MAX_RDATA_LENGTH 65535 /*! \brief Maximal length of rdata item. */ @@ -170,9 +172,7 @@ struct scanner { /*! Absolute path for relative includes. */ char *path; - /*! Zone file name, if specified. */ - char *file_name; - /*! Zone file line counter. */ + /*! Zone data line counter. */ uint64_t line_counter; /*! Last occured error/warning code. */ int error_code; @@ -184,6 +184,13 @@ struct scanner { */ bool stop; + struct { + /*! Zone file name. */ + char *name; + /*!< File descriptor. */ + int descriptor; + } file; + /*! * Owner of the current record. * @@ -220,8 +227,6 @@ struct scanner { /*! * \brief Creates zone scanner structure. * - * \param file_name Name of file to process (NULL if parsing from - * memory). * \param origin Initial zone origin. * \param rclass Zone class value. * \param ttl Initial ttl value. @@ -229,11 +234,10 @@ struct scanner { * \param process_error Error callback function. * \param data Arbitrary data useful in callback functions. * - * \retval scanner if success. - * \retval 0 if error. + * \retval scanner if success. + * \retval NULL if error. */ -zs_scanner_t* zs_scanner_create(const char *file_name, - const char *origin, +zs_scanner_t* zs_scanner_create(const char *origin, const uint16_t rclass, const uint32_t ttl, void (*process_record)(zs_scanner_t *), @@ -248,23 +252,41 @@ zs_scanner_t* zs_scanner_create(const char *file_name, void zs_scanner_free(zs_scanner_t *scanner); /*! - * \brief Executes zone scanner on data block. + * \brief Parser memory block with zone data. + * + * For each correctly recognized record data process_record callback function + * is called. If any syntax error occures, then process_error callback + * function is called. * * \note Zone scanner error code and other information are stored in * the scanner structure. * - * \param start First byte of the zone data to scan. - * \param end Last byte of the zone data to scan. - * \param is_complete Indicates if the current block is complete i.e. the - * last record line doesn't continue in the next block. - * \param scanner Zone scanner structure. + * \param scanner Zone scanner. + * \param start First byte of the zone data to parse. + * \param end Last byte of the zone data to parse. + * \param final_block Indicates if the current block is final i.e. no + * other blocks will be processed. * * \retval 0 if success. * \retval -1 if error. */ -int zs_scanner_process(const char *start, - const char *end, - const bool is_complete, - zs_scanner_t *scanner); +int zs_scanner_parse(zs_scanner_t *scanner, + const char *start, + const char *end, + const bool final_block); +/*! + * \brief Parses specified zone file. + * + * \note Zone scanner error code and other information are stored in + * the scanner structure. + * + * \param scanner Zone scanner. + * \param file_name Name of file to process. + * + * \retval 0 if success. + * \retval -1 if error. + */ +int zs_scanner_parse_file(zs_scanner_t *scanner, + const char *file_name); /*! @} */ diff --git a/src/zscanner/scanner.rl b/src/zscanner/scanner.rl index cdb7c95c81c205de6756501ba67fa5e7bebbec08..2bcca2a16ed0191a1412a26db63a7ba37de12c73 100644 --- a/src/zscanner/scanner.rl +++ b/src/zscanner/scanner.rl @@ -18,21 +18,31 @@ #include <stdint.h> // uint32_t #include <stdlib.h> // calloc #include <stdio.h> // sprintf +#include <fcntl.h> // open #include <libgen.h> // dirname #include <stdbool.h> // bool -#include <math.h> // pow #include <string.h> // strdup +#include <math.h> // pow +#include <sys/mman.h> // mmap #include <sys/types.h> // (OpenBSD) #include <sys/socket.h> // AF_INET (BSD) +#include <sys/stat.h> // fstat #include <netinet/in.h> // in_addr (BSD) #include <arpa/inet.h> // inet_pton +#include <unistd.h> // sysconf #include "zscanner/scanner.h" -#include "zscanner/loader.h" // loader in includes -#include "zscanner/error.h" #include "zscanner/functions.h" #include "libknot/descriptor.h" +/*! \brief Mmap block size in bytes. This value is then adjusted to the + * multiple of memory pages which fit in. + */ +#define BLOCK_SIZE 30000000 + +/*! \brief Last artificial block which ensures final newline character. */ +#define NEWLINE_BLOCK "\n" + /*! \brief Shorthand for setting warning data. */ #define WARN(code) { s->error_code = code; } /*! \brief Shorthand for setting error data. */ @@ -89,8 +99,7 @@ static inline void window_add_bit(const uint16_t type, zs_scanner_t *s) { write data; }%% -zs_scanner_t* zs_scanner_create(const char *file_name, - const char *origin, +zs_scanner_t* zs_scanner_create(const char *origin, const uint16_t rclass, const uint32_t ttl, void (*process_record)(zs_scanner_t *), @@ -99,26 +108,13 @@ zs_scanner_t* zs_scanner_create(const char *file_name, { char settings[1024]; - zs_scanner_t *s = calloc(1, sizeof(zs_scanner_t)); - if (s == NULL) { + if (origin == NULL) { return NULL; } - if (file_name != NULL) { - // Get absolute path of the zone file. - if (realpath(file_name, (char*)(s->buffer)) != NULL) { - char *full_name = strdup((char*)(s->buffer)); - s->path = strdup(dirname(full_name)); - free(full_name); - } else { - free(s); - return NULL; - } - - s->file_name = strdup(file_name); - } else { - s->path = strdup("."); - s->file_name = strdup("<NULL>"); + zs_scanner_t *s = calloc(1, sizeof(zs_scanner_t)); + if (s == NULL) { + return NULL; } // Nonzero initial scanner state. @@ -138,7 +134,7 @@ zs_scanner_t* zs_scanner_create(const char *file_name, } int ret = snprintf(settings, sizeof(settings), format, origin); if (ret <= 0 || (size_t)ret >= sizeof(settings) || - zs_scanner_process(settings, settings + ret, true, s) != 0) { + zs_scanner_parse(s, settings, settings + ret, true) != 0) { zs_scanner_free(s); return NULL; } @@ -149,6 +145,7 @@ zs_scanner_t* zs_scanner_create(const char *file_name, s->process_record = process_record ? process_record : &noop; s->process_error = process_error ? process_error : &noop; s->data = data; + s->path = strdup("."); s->line_counter = 1; return s; @@ -157,16 +154,15 @@ zs_scanner_t* zs_scanner_create(const char *file_name, void zs_scanner_free(zs_scanner_t *s) { if (s != NULL) { - free(s->file_name); free(s->path); free(s); } } -int zs_scanner_process(const char *start, - const char *end, - const bool is_complete, - zs_scanner_t *s) +static void parse_block(zs_scanner_t *s, + const char *start, + const char *end, + const bool is_eof) { // Necessary scanner variables. const char *p = start; @@ -193,7 +189,7 @@ int zs_scanner_process(const char *start, memcpy(stack, s->stack, sizeof(stack)); // End of file check. - if (is_complete == true) { + if (is_eof) { eof = (char *)pe; } @@ -224,11 +220,11 @@ int zs_scanner_process(const char *start, // Processing error. s->process_error(s); - return -1; + return; } // Check unclosed multiline record. - if (is_complete && s->multiline) { + if (is_eof && s->multiline) { ERR(ZS_UNCLOSED_MULTILINE); s->error_counter++; s->process_error(s); @@ -241,6 +237,24 @@ int zs_scanner_process(const char *start, // Storing r_data pointer. s->r_data_tail = rdata_tail - s->r_data; +} + +int zs_scanner_parse(zs_scanner_t *s, + const char *start, + const char *end, + const bool final_block) +{ + if (s == NULL || start == NULL || end == NULL) { + return -1; + } + + // Parse input block. + parse_block(s, start, end, false); + + // Parse trailing artificial block (newline char) if not stop. + if (final_block && !s->stop) { + parse_block(s, NEWLINE_BLOCK, NEWLINE_BLOCK + 1, true); + } // Check if any errors has occured. if (s->error_counter > 0) { @@ -249,3 +263,120 @@ int zs_scanner_process(const char *start, return 0; } + +int zs_scanner_parse_file(zs_scanner_t *s, + const char *file_name) +{ + long page_size; + uint64_t n_blocks; + uint64_t block_id; + uint64_t default_block_size; + struct stat file_stat; + + if (s == NULL || file_name == NULL) { + return -1; + } + + // Copying file name. + s->file.name = strdup(file_name); + + // Opening the zone file. + s->file.descriptor = open(file_name, O_RDONLY); + if (s->file.descriptor == -1) { + ERR(ZS_FILE_OPEN); + free(s->file.name); + return -1; + } + + // Get absolute path of the zone file. + if (realpath(file_name, (char*)(s->buffer)) != NULL) { + char *full_name = strdup((char*)(s->buffer)); + free(s->path); + s->path = strdup(dirname(full_name)); + free(full_name); + } else { + ERR(ZS_FILE_PATH); + close(s->file.descriptor); + free(s->file.name); + return -1; + } + + // Getting OS page size. + page_size = sysconf(_SC_PAGESIZE); + + // Getting file information. + if (fstat(s->file.descriptor, &file_stat) == -1) { + ERR(ZS_FILE_FSTAT); + close(s->file.descriptor); + free(s->file.name); + return -1; + } + + // Check for directory. + if (S_ISDIR(file_stat.st_mode)) { + ERR(ZS_FILE_DIRECTORY); + close(s->file.descriptor); + free(s->file.name); + return -1; + } + + // Block size adjustment to multiple of page size. + default_block_size = (BLOCK_SIZE / page_size) * page_size; + + // Number of blocks which cover the whole file (ceiling operation). + n_blocks = 1 + ((file_stat.st_size - 1) / default_block_size); + + // Loop over zone file blocks. + for (block_id = 0; block_id < n_blocks; block_id++) { + // Current block start to scan. + uint64_t scanner_start = block_id * default_block_size; + // Current block size to scan. + uint64_t block_size = default_block_size; + // Last data block mark. + bool final_block = false; + // Mmapped data. + char *data; + + // The last block is probably shorter. + if (block_id == (n_blocks - 1)) { + block_size = file_stat.st_size - scanner_start; + final_block = true; + } + + // Zone file block mapping. + data = mmap(0, block_size, PROT_READ, MAP_SHARED, + s->file.descriptor, scanner_start); + if (data == MAP_FAILED) { + ERR(ZS_FILE_MMAP); + close(s->file.descriptor); + free(s->file.name); + return -1; + } + + // Scan zone file block. + zs_scanner_parse(s, data, data + block_size, final_block); + + // Zone file block unmapping. + if (munmap(data, block_size) == -1) { + ERR(ZS_FILE_MMAP); + close(s->file.descriptor); + free(s->file.name); + return -1; + } + + // Stop parsing if required. + if (s->stop) { + break; + } + } + + close(s->file.descriptor); + free(s->file.name); + + // Check for scanner return. + if (s->error_counter > 0) { + return -1; + } + + return 0; +} diff --git a/src/zscanner/scanner_body.rl b/src/zscanner/scanner_body.rl index 31500f9c59d3e1cc6e1b883891c7d3c8249afb1d..71bbe0ee6fff5a1a8f7e24bb51ae9216d82451dc 100644 --- a/src/zscanner/scanner_body.rl +++ b/src/zscanner/scanner_body.rl @@ -95,7 +95,7 @@ // In case of serious error, stop scanner. if (s->stop == true) { - return -1; + return; } } @@ -661,25 +661,30 @@ sizeof(s->buffer)); } - // Create new file loader for included zone file. - zs_loader_t *fl = zs_loader_create((char*)(s->buffer), - text_origin, - s->default_class, - s->default_ttl, - s->process_record, - s->process_error, - s->data); - if (fl != NULL) { - // Process included zone file. - ret = zs_loader_process(fl); - zs_loader_free(fl); - + // Create new scanner for included zone file. + zs_scanner_t *ss = zs_scanner_create(text_origin, + s->default_class, + s->default_ttl, + s->process_record, + s->process_error, + s->data); + if (ss != NULL) { + // Parse included zone file. + ret = zs_scanner_parse_file(ss, (char*)(s->buffer)); if (ret != 0) { - ERR(ZS_UNPROCESSED_INCLUDE); + // File internal errors are handled by error callback. + if (ss->error_counter > 0) { + ERR(ZS_UNPROCESSED_INCLUDE); + // General include file error. + } else { + ERR(ss->error_code); + } + zs_scanner_free(ss); fhold; fgoto err_line; } + zs_scanner_free(ss); } else { - ERR(ZS_UNOPENED_INCLUDE); + ERR(ZS_UNPROCESSED_INCLUDE); fhold; fgoto err_line; } } @@ -1907,7 +1912,7 @@ // Stop scanner if required. if (s->stop == true) { - return -1; + return; } } diff --git a/src/zscanner/tests/data/06-1_INCLUDE.in b/src/zscanner/tests/data/06-1_INCLUDE.in index 0eb3b64932151a59f8071a3c27ef20d3a02d1261..194fb5292f4526d4f4bf6fa55ab8bc5407b8502d 100644 --- a/src/zscanner/tests/data/06-1_INCLUDE.in +++ b/src/zscanner/tests/data/06-1_INCLUDE.in @@ -2,4 +2,4 @@ $ORIGIN . $TTL 1 ; KO -$INCLUDE ./includes/include1 a ; Origin is not FQD +$INCLUDE ./includes/include1 a ; Origin is not FQDN diff --git a/src/zscanner/tests/data/06-3_INCLUDE.out b/src/zscanner/tests/data/06-3_INCLUDE.out index 965597d62c1a81c0d6b08a538dc91bb9e11118d5..8a5289b0bca31bf9482c21fa6ba07290f39d947d 100644 --- a/src/zscanner/tests/data/06-3_INCLUDE.out +++ b/src/zscanner/tests/data/06-3_INCLUDE.out @@ -1,2 +1,2 @@ -ERROR=ZS_UNPROCESSED_INCLUDE +ERROR=ZS_FILE_DIRECTORY ------ diff --git a/src/zscanner/tests/data/06-4_INCLUDE.out b/src/zscanner/tests/data/06-4_INCLUDE.out index 5fec4afbdd65cd6badc18ef550609c77fbc7eccd..e09e5d1cfc18ed8606c48a1395e3c7603b9de72d 100644 --- a/src/zscanner/tests/data/06-4_INCLUDE.out +++ b/src/zscanner/tests/data/06-4_INCLUDE.out @@ -1,2 +1,2 @@ -ERROR=ZS_UNOPENED_INCLUDE +ERROR=ZS_FILE_OPEN ------ diff --git a/src/zscanner/tests/data/06-5_INCLUDE.out b/src/zscanner/tests/data/06-5_INCLUDE.out index 965597d62c1a81c0d6b08a538dc91bb9e11118d5..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/src/zscanner/tests/data/06-5_INCLUDE.out +++ b/src/zscanner/tests/data/06-5_INCLUDE.out @@ -1,2 +0,0 @@ -ERROR=ZS_UNPROCESSED_INCLUDE ------- diff --git a/src/zscanner/tests/processing.c b/src/zscanner/tests/processing.c index 2c6c429c8a98a53ba108b12eb01cfe9e991dada9..9bc3e1371fb97bca8ed23919a8a627e7d7aa1efd 100644 --- a/src/zscanner/tests/processing.c +++ b/src/zscanner/tests/processing.c @@ -18,7 +18,6 @@ #include <stdio.h> // printf #include "tests/processing.h" -#include "error.h" #include "scanner.h" #include "libknot/descriptor.c" // knot_rrtype_to_string (implementation) @@ -45,13 +44,13 @@ void debug_process_error(zs_scanner_t *s) printf("LINE(%03"PRIu64") ERROR(%s) FILE(%s) NEAR(%s)\n", s->line_counter, zs_strerror(s->error_code), - s->file_name, + s->file.name, s->buffer); } else { printf("LINE(%03"PRIu64") WARNING(%s) FILE(%s) NEAR(%s)\n", s->line_counter, zs_strerror(s->error_code), - s->file_name, + s->file.name, s->buffer); } fflush(stdout); diff --git a/src/zscanner/tests/zscanner-tool.c b/src/zscanner/tests/zscanner-tool.c index e28beaa0b6ed1b42a47bf5172146fed155d46fdd..4bdbacfab34e37d6dbcf99b7c22f6c399188038c 100644 --- a/src/zscanner/tests/zscanner-tool.c +++ b/src/zscanner/tests/zscanner-tool.c @@ -20,8 +20,7 @@ #include <getopt.h> // getopt #include <pthread.h> // pthread_t -#include "error.h" -#include "loader.h" +#include "scanner.h" #include "tests/processing.h" #include "tests/tests.h" @@ -50,6 +49,26 @@ static void help(void) " -h Print this help.\n"); } +static int time_test() +{ + pthread_t t1, t2, t3; + int ret1, ret2, ret3; + + pthread_create(&t1, NULL, timestamp_worker, &ret1); + pthread_create(&t2, NULL, timestamp_worker, &ret2); + pthread_create(&t3, NULL, timestamp_worker, &ret3); + + pthread_join(t1, NULL); + pthread_join(t2, NULL); + pthread_join(t3, NULL); + + if (ret1 != 0 || ret2 != 0 || ret3 != 0) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + int main(int argc, char *argv[]) { // Parsed command line arguments. @@ -83,101 +102,74 @@ int main(int argc, char *argv[]) } if (test == 1) { - pthread_t t1, t2, t3; - int ret1, ret2, ret3; - - pthread_create(&t1, NULL, timestamp_worker, &ret1); - pthread_create(&t2, NULL, timestamp_worker, &ret2); - pthread_create(&t3, NULL, timestamp_worker, &ret3); + return time_test(); + } - pthread_join(t1, NULL); - pthread_join(t2, NULL); - pthread_join(t3, NULL); + // Check if there are 2 remaining non-options. + if (argc - optind != 2) { + help(); + return EXIT_FAILURE; + } - if (ret1 != 0 || ret2 != 0 || ret3 != 0) { - return EXIT_FAILURE; - } - } else { - zs_loader_t *fl; + const char *origin = argv[optind]; + const char *zone_file = argv[optind + 1]; + zs_scanner_t *s; + + // Create appropriate zone scanner. + switch (mode) { + case 0: + s = zs_scanner_create(origin, + DEFAULT_CLASS, + DEFAULT_TTL, + NULL, + NULL, + NULL); + break; + case 1: + s = zs_scanner_create(origin, + DEFAULT_CLASS, + DEFAULT_TTL, + &debug_process_record, + &debug_process_error, + NULL); + break; + case 2: + s = zs_scanner_create(origin, + DEFAULT_CLASS, + DEFAULT_TTL, + &test_process_record, + &test_process_error, + NULL); + break; + default: + printf("Bad mode number!\n"); + help(); + return EXIT_FAILURE; + } - // Check if there are 2 remaining non-options. - if (argc - optind != 2) { - help(); - return EXIT_FAILURE; - } + // Check parser creation. + if (s == NULL) { + printf("Scanner create error!\n"); + return EXIT_FAILURE; + } - const char *origin = argv[optind]; - const char *zone_file = argv[optind + 1]; - - // Create appropriate file loader. - switch (mode) { - case 0: - fl = zs_loader_create(zone_file, - origin, - DEFAULT_CLASS, - DEFAULT_TTL, - NULL, - NULL, - NULL); - break; - case 1: - fl = zs_loader_create(zone_file, - origin, - DEFAULT_CLASS, - DEFAULT_TTL, - &debug_process_record, - &debug_process_error, - NULL); - break; - case 2: - fl = zs_loader_create(zone_file, - origin, - DEFAULT_CLASS, - DEFAULT_TTL, - &test_process_record, - &test_process_error, - NULL); - break; - default: - printf("Bad mode number!\n"); - help(); - return EXIT_FAILURE; + // Parse the file. + int ret = zs_scanner_parse_file(s, zone_file); + if (ret == 0) { + if (mode == DEFAULT_MODE) { + printf("Zone file has been processed successfully\n"); } - - // Check file loader. - if (fl != NULL) { - int ret = zs_loader_process(fl); - - switch (ret) { - case ZS_OK: - if (mode == DEFAULT_MODE) { - printf("Zone file has been processed " - "successfully\n"); - } - zs_loader_free(fl); - break; - - case ZS_LOADER_SCANNER: - if (mode == DEFAULT_MODE) { - printf("Zone processing has stopped with " - "%"PRIu64" warnings/errors!\n", - fl->scanner->error_counter); - } - zs_loader_free(fl); - return EXIT_FAILURE; - - default: - if (mode == DEFAULT_MODE) { - printf("%s\n", zs_strerror(ret)); - } - zs_loader_free(fl); - return EXIT_FAILURE; - } - } else { - printf("File open error!\n"); - return EXIT_FAILURE; + zs_scanner_free(s); + return EXIT_SUCCESS; + } else { + if (s->error_counter > 0 && mode == DEFAULT_MODE) { + printf("Zone processing has stopped with " + "%"PRIu64" warnings/errors!\n", + s->error_counter); + } else if (mode == DEFAULT_MODE) { + printf("%s\n", zs_strerror(s->error_code)); } + zs_scanner_free(s); + return EXIT_FAILURE; } - - return EXIT_SUCCESS; } diff --git a/src/zscanner/zscanner.h b/src/zscanner/zscanner.h deleted file mode 100644 index 56566385636d5a7f7033e82fd0095d3072661e26..0000000000000000000000000000000000000000 --- a/src/zscanner/zscanner.h +++ /dev/null @@ -1,31 +0,0 @@ -/* 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/>. - */ -/*! - * \file zscanner.h - * - * \author Daniel Salzman <daniel.salzman@nic.cz> - * - * \brief Public interface for zscanner. - * @{ - */ - -#pragma once - -#include "zscanner/error.h" -#include "zscanner/scanner.h" -#include "zscanner/loader.h" - -/*! @} */ diff --git a/tests-extra/README b/tests-extra/README index 94e3733431644a240db360ea764709c4dfd8d0a2..11621a99197adfbce63d38c50dc81804fd8a66bb 100644 --- a/tests-extra/README +++ b/tests-extra/README @@ -11,6 +11,12 @@ lsof (valgrind) (gdb) +Python modules: +--------------- + +To install necessary Python modules using pip, run: +$ pip install -r requirements.txt + Ubuntu: ------- Disable apparmor protection for system Bind: diff --git a/tests-extra/data/wild.zone b/tests-extra/data/wild.zone index 0edd42d65c83f93ce0fc05f8fba055a9808ecdab..13d58a72aea29bfdc131159a16619a5199e1ac03 100644 --- a/tests-extra/data/wild.zone +++ b/tests-extra/data/wild.zone @@ -1,7 +1,7 @@ $ORIGIN wild. $TTL 3600 -@ SOA dns1 hostmaster 2010111213 10800 3600 1209600 7200 +@ SOA dns1 Hostmaster 2010111213 10800 3600 1209600 7200 NS dns1 NS dns2 MX 10 mail @@ -23,3 +23,5 @@ test HINFO "" "" TXT "\\" "\"" TXT "ahoj" "" "nevim" NAPTR 20 0 "s" "SIP+D2U" "" sip._udp.wild. + +Upper-case MX 10 MaiL diff --git a/tests-extra/requirements.txt b/tests-extra/requirements.txt index 5c6c6d5651c5e6d684b2b8e8beaf058c19ddf089..a42bc44e35e7983531bd460bffa835332b93d5dc 100644 --- a/tests-extra/requirements.txt +++ b/tests-extra/requirements.txt @@ -1,2 +1,3 @@ -dnspython3==1.11.1 +#dnspython3==1.11.1 +-e git+https://github.com/rthalley/dnspython.git@python3#egg=dnspython3 psutil==1.2.1 diff --git a/tests-extra/tests/dnssec/dnskey_algorithms/data/generate_keys.sh b/tests-extra/tests/dnssec/dnskey_algorithms/data/generate_keys.sh new file mode 100755 index 0000000000000000000000000000000000000000..cfd52b0ec02b745eeec14c7939865b0b4bd47844 --- /dev/null +++ b/tests-extra/tests/dnssec/dnskey_algorithms/data/generate_keys.sh @@ -0,0 +1,68 @@ +#!/bin/sh +# +# Run this script every 50 years to refresh the keys. :-) +# + +set -xe + +TIME_PAST="-50y" +TIME_FUTURE="+50y" + +keygen() +{ + dnssec-keygen -r/dev/urandom $@ +} + +dir=$(pwd) +keydir=$(mktemp -d) + +pushd "$keydir" + +# +# valid scenarios +# + +keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_ok +keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST rsa_ok + +keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST rsa_ecdsa_ok +keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_ecdsa_ok +keygen -a ECDSAP256SHA256 -P $TIME_PAST -A $TIME_PAST rsa_ecdsa_ok +keygen -a ECDSAP256SHA256 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_ecdsa_ok + +keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_ecdsa_roll_ok +keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST rsa_ecdsa_roll_ok +keygen -a ECDSAP256SHA256 -P $TIME_FUTURE -A $TIME_PAST rsa_ecdsa_roll_ok + +# +# invalid scenarios +# + +keygen -a RSASHA256 -b 2048 -P $TIME_FUTURE -A $TIME_FUTURE -f KSK rsa_future_all +keygen -a RSASHA256 -b 1024 -P $TIME_FUTURE -A $TIME_FUTURE rsa_future_all + +keygen -a RSASHA512 -b 2048 -P $TIME_FUTURE -A $TIME_PAST -f KSK rsa_future_publish +keygen -a RSASHA256 -b 1024 -P $TIME_FUTURE -A $TIME_PAST rsa_future_publish + +keygen -a RSASHA512 -b 2048 -P $TIME_PAST -A $TIME_FUTURE -f KSK rsa_future_active +keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_FUTURE rsa_future_active + +keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_inactive_zsk +keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_FUTURE rsa_inactive_zsk + +keygen -a RSASHA256 -b 2048 -P $TIME_FUTURE -A $TIME_FUTURE -f KSK rsa_no_zsk +keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST rsa_no_zsk + +keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_twice_ksk +keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_twice_ksk + +keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_ecdsa_ksk_only +keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST rsa_ecdsa_ksk_only +keygen -a ECDSAP256SHA256 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_ecdsa_ksk_only + +keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa256_rsa512 +keygen -a RSASHA512 -b 2048 -P $TIME_PAST -A $TIME_PAST rsa256_rsa512 + +tar czf "$dir/keys.tgz" K*.{key,private} +popd +rm -rf "$keydir" diff --git a/tests-extra/tests/dnssec/dnskey_algorithms/data/keys.tgz b/tests-extra/tests/dnssec/dnskey_algorithms/data/keys.tgz new file mode 100644 index 0000000000000000000000000000000000000000..25aef643afd6f39ff8f00ea290eb7016b4300bac Binary files /dev/null and b/tests-extra/tests/dnssec/dnskey_algorithms/data/keys.tgz differ diff --git a/tests-extra/tests/dnssec/dnskey_algorithms/test.py b/tests-extra/tests/dnssec/dnskey_algorithms/test.py new file mode 100644 index 0000000000000000000000000000000000000000..20b81d4c4c10535f0cfb2c377971067a3fa8c736 --- /dev/null +++ b/tests-extra/tests/dnssec/dnskey_algorithms/test.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +""" +Validate ZSK and KSK constrains checks. +""" + +import tarfile +import os.path + +import dnstest.zonefile +from dnstest.test import Test + +TEST_CASES = { + # valid cases + "rsa_ok": True, + "rsa_ecdsa_ok": True, + "rsa_ecdsa_roll_ok": True, + # invalid cases + "rsa_future_all": False, + "rsa_future_publish": False, + "rsa_future_active": False, + "rsa_inactive_zsk": False, + "rsa_no_zsk": False, + "rsa_twice_ksk": False, + "rsa_ecdsa_ksk_only": False, + "rsa256_rsa512": False, +} + +t = Test() + +knot = t.server("knot") +knot.dnssec_enable = True + +# setup keys + +keys_archive = os.path.join(t.data_dir, "keys.tgz") +with tarfile.open(keys_archive, "r:*") as tar: + tar.extractall(knot.keydir) + +# setup zones + +zones = [] +for zone_name in TEST_CASES: + zone = dnstest.zonefile.ZoneFile(t.zones_dir) + zone.set_name(zone_name) + zone.gen_file(dnssec=False, nsec3=False, records=5) + zones.append(zone) + +t.link(zones, knot) + +t.start() + +for zone, valid in TEST_CASES.items(): + expected_rcode = "NOERROR" if valid else "SERVFAIL" + knot.dig(zone, "SOA").check(rcode=expected_rcode) + +t.end() diff --git a/tests-extra/tests/dnssec/no_resign/test.py b/tests-extra/tests/dnssec/no_resign/test.py index 7e344180df5f9770c13125d4e9c26842ebe29d44..b6b7395d179e134a1523c29f39b10a56261e10e7 100644 --- a/tests-extra/tests/dnssec/no_resign/test.py +++ b/tests-extra/tests/dnssec/no_resign/test.py @@ -24,6 +24,7 @@ master.dnssec_enable = True master.use_keys(nsec_zone) master.use_keys(nsec3_zone) master.gen_confile() +t.sleep(2) master.reload() t.sleep(4) diff --git a/tests-extra/tests/edns/version/test.py b/tests-extra/tests/edns/version/test.py new file mode 100644 index 0000000000000000000000000000000000000000..69dacb4b561721a2394f66c4bc88a801297e5af9 --- /dev/null +++ b/tests-extra/tests/edns/version/test.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +'''Test for EDNS version''' + +from dnstest.test import Test + +t = Test() + +server = t.server("knot") +zone = t.zone("example.com.") + +t.link(zone, server) + +t.start() + +# Supported EDNS version 0. +resp = server.dig("example.com", "SOA", edns=0) +resp.check(rcode="NOERROR", edns_version=0) + +# Unsupported EDNS version 1. +resp = server.dig("example.com", "SOA", edns=1) +resp.check(rcode="BADVERS", edns_version=0) + +t.end() diff --git a/tests-extra/tools/dnstest/response.py b/tests-extra/tools/dnstest/response.py index 41cebc32515725ece2ea8d4108f1e5791e290bfe..51b014f7fbcf1a6ff693dcc5ba5341e971479fe0 100644 --- a/tests-extra/tools/dnstest/response.py +++ b/tests-extra/tools/dnstest/response.py @@ -8,8 +8,9 @@ from dnstest.utils import * class Response(object): '''Dig output context.''' - def __init__(self, server, response, args): + def __init__(self, server, response, query, args): self.resp = response + self.query = query self.args = args self.srv = server @@ -113,13 +114,17 @@ class Response(object): return def check(self, rdata=None, ttl=None, rcode="NOERROR", nordata=None, - flags="", noflags="", eflags="", noeflags=""): + edns_version=None, flags="", noflags="", eflags="", noeflags=""): '''Flags are text strings separated by whitespace character''' self._check_flags(flags, noflags) self._check_eflags(eflags, noeflags) self._check_question() + # Check EDNS version. + edns_ver = int(edns_version) if edns_version != None else self.query.edns + compare(edns_ver, self.resp.edns, "EDNS VERSION") + # Check rcode. if type(rcode) is not str: rc = dns.rcode.to_text(rcode) diff --git a/tests-extra/tools/dnstest/server.py b/tests-extra/tools/dnstest/server.py index 62ece8ade7e3f6069484fb455c25d8d3ec220298..5fa5adc762f4830820d23056c5e85219a477126c 100644 --- a/tests-extra/tools/dnstest/server.py +++ b/tests-extra/tools/dnstest/server.py @@ -393,7 +393,7 @@ class Server(object): f.close def dig(self, rname, rtype, rclass="IN", udp=None, serial=None, - timeout=None, tries=3, flags="", bufsize=None, + timeout=None, tries=3, flags="", bufsize=None, edns=None, nsid=False, dnssec=False, log_no_sep=False): key_params = self.tsig_test.key_params if self.tsig_test else dict() @@ -458,7 +458,7 @@ class Server(object): dig_flags += " +cd" # Set EDNS. - if nsid or bufsize: + if edns != None or bufsize or nsid: class NsidFix(object): '''Current pythondns doesn't implement NSID option.''' def __init__(self): @@ -466,6 +466,12 @@ class Server(object): def to_wire(self, file=None): pass + if edns: + edns = int(edns) + else: + edns = 0 + dig_flags += " +edns=%i" % edns + if bufsize: payload = int(bufsize) else: @@ -478,7 +484,7 @@ class Server(object): else: options = None - query.use_edns(edns=0, payload=payload, options=options) + query.use_edns(edns=edns, payload=payload, options=options) # Set DO flag. if dnssec: @@ -519,7 +525,7 @@ class Server(object): if not log_no_sep: detail_log(SEP) - return dnstest.response.Response(self, resp, args) + return dnstest.response.Response(self, resp, query, args) except dns.exception.Timeout: pass except: @@ -628,7 +634,7 @@ class Server(object): src_files = os.listdir(zone.key_dir) for file_name in src_files: - if zone.name[:-1] in file_name: + if (zone.name[:-1]).lower() in file_name: full_file_name = os.path.join(zone.key_dir, file_name) if (os.path.isfile(full_file_name)): shutil.copy(full_file_name, self.keydir) diff --git a/tests-extra/tools/zone_generate.py b/tests-extra/tools/zone_generate.py index e0c24cf8c98fc2fb94bc270bdf57936d48b7cc6a..1b290e6c0f048939c7fa6ab861a9f86e56cf44d1 100755 --- a/tests-extra/tools/zone_generate.py +++ b/tests-extra/tools/zone_generate.py @@ -16,6 +16,7 @@ Parameters: import binascii import getopt +import string import os import random import re @@ -89,6 +90,12 @@ WORDS = [ "mobile","customer","siprouter","sip","office","voice","support", "spare","owa","exchange" ] +# Replace some words with random ones +for i, word in enumerate(WORDS): + if random.choice([True, False]): + size = random.randint(2, 20) + WORDS[i] = ''.join(random.choice(string.hexdigits) for _ in range(size)) + # For unique CNAMES/DNAMES CNAME_EXIST = set([]) # For unique names diff --git a/tests/Makefile.am b/tests/Makefile.am index caa57e9464781f136187c4a9936b8dd11499ac24..a8861f1d0816c39da0fc1ec9eeeb1db2f6af10d7 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -27,6 +27,7 @@ check_PROGRAMS = \ ztree \ zonedb \ changeset \ + zone-update \ dnssec_keys \ dnssec_nsec3 \ dnssec_sign \ diff --git a/tests/fake_server.h b/tests/fake_server.h index 0f3bea7ad0d4c4f91e10ed8f61db5e0eb74d2ee9..9026b2a321ca289ba6d3371bbaea2d95a7b133ba 100644 --- a/tests/fake_server.h +++ b/tests/fake_server.h @@ -33,7 +33,7 @@ static inline void create_root_zone(server_t *server, mm_ctx_t *mm) knot_rrset_t *soa = knot_rrset_new(root->name, KNOT_RRTYPE_SOA, KNOT_CLASS_IN, mm); knot_rrset_add_rdata(soa, SOA_RDATA, SOA_RDLEN, 7200, mm); - node_add_rrset(root->contents->apex, soa); + node_add_rrset(root->contents->apex, soa, NULL); knot_rrset_free(&soa, mm); /* Bake the zone. */ diff --git a/tests/hattrie.c b/tests/hattrie.c index 6f8bd61b0aea41878cd821c76040d107c5a27505..cb8d9c241ea7a1090edc44bf7b536d4c9100728a 100644 --- a/tests/hattrie.c +++ b/tests/hattrie.c @@ -101,7 +101,7 @@ static bool str_key_find_leq(hattrie_t *trie, char **keys, size_t i, size_t size int main(int argc, char *argv[]) { - plan(7); + plan(8); /* Random keys. */ srand(time(NULL)); @@ -171,7 +171,16 @@ int main(int argc, char *argv[]) } } ok(passed, "hattrie: find lesser or equal for all keys"); - + + /* Next lookup. */ + passed = true; + for (unsigned i = 0; i < key_count - 1 && passed; ++i) { + value_t *val; + hattrie_find_next(trie, keys[i], strlen(keys[i]), &val); + passed = val && *val == (void *)keys[(i + 1)]; + } + ok(passed, "hattrie: find next for all keys"); + /* Unsorted iteration */ size_t iterated = 0; hattrie_iter_t *it = hattrie_iter_begin(trie, false); diff --git a/tests/node.c b/tests/node.c index 29a2ef84595194500a52882e0b9771520db4e251..3d527721d8a6dabb34a1a4eefd0aa38575577ac8 100644 --- a/tests/node.c +++ b/tests/node.c @@ -52,28 +52,28 @@ int main(int argc, char *argv[]) knot_dname_t *dummy_owner = knot_dname_from_str("test."); // Test new - zone_node_t *node = node_new(dummy_owner); + zone_node_t *node = node_new(dummy_owner, NULL); ok(node != NULL, "Node: new"); assert(node); ok(knot_dname_is_equal(node->owner, dummy_owner), "Node: new - set fields"); // Test parent setting - zone_node_t *parent = node_new(dummy_owner); + zone_node_t *parent = node_new(dummy_owner, NULL); assert(parent); node_set_parent(node, parent); ok(node->parent == parent && parent->children == 1, "Node: set parent."); - node_free(&parent); + node_free(&parent, NULL); // Test RRSet addition knot_rrset_t *dummy_rrset = create_dummy_rrset(dummy_owner, KNOT_RRTYPE_TXT); - int ret = node_add_rrset(node, dummy_rrset); + int ret = node_add_rrset(node, dummy_rrset, NULL); ok(ret == KNOT_EOK && node->rrset_count == 1 && knot_rdataset_eq(&dummy_rrset->rrs, &node->rrs[0].rrs), "Node: add RRSet."); // Test shallow copy node->flags |= NODE_FLAGS_DELEG; - zone_node_t *copy = node_shallow_copy(node); + zone_node_t *copy = node_shallow_copy(node, NULL); ok(copy != NULL, "Node: shallow copy."); assert(copy); const bool copy_ok = knot_dname_is_equal(copy->owner, node->owner) && @@ -83,7 +83,7 @@ int main(int argc, char *argv[]) copy->flags == node->flags; ok(copy_ok, "Node: shallow copy - set fields."); - node_free(©); + node_free(©, NULL); // Test RRSet getters knot_rrset_t *n_rrset = node_create_rrset(node, KNOT_RRTYPE_TXT); @@ -116,7 +116,7 @@ int main(int argc, char *argv[]) // Test TTL mismatch knot_rdata_t *data = knot_rdataset_at(&dummy_rrset->rrs, 0); knot_rdata_set_ttl(data, 1800); - ret = node_add_rrset(node, dummy_rrset); + ret = node_add_rrset(node, dummy_rrset, NULL); ok(ret == KNOT_ETTL && node->rrset_count == 1, "Node: add RRSet, TTL mismatch."); @@ -128,7 +128,7 @@ int main(int argc, char *argv[]) ok(!node_rrtype_is_signed(node, KNOT_RRTYPE_TXT), "Node: type is not signed."); dummy_rrset = create_dummy_rrsig(dummy_owner, KNOT_RRTYPE_TXT); - ret = node_add_rrset(node, dummy_rrset); + ret = node_add_rrset(node, dummy_rrset, NULL); assert(ret == KNOT_EOK); ok(node_rrtype_is_signed(node, KNOT_RRTYPE_TXT), "Node: type is signed."); @@ -146,10 +146,10 @@ int main(int argc, char *argv[]) free(to_free); // "Test" freeing - node_free_rrsets(node); + node_free_rrsets(node, NULL); ok(node->rrset_count == 0, "Node: free RRSets."); - node_free(&node); + node_free(&node, NULL); ok(node == NULL, "Node: free."); knot_dname_free(&dummy_owner, NULL); diff --git a/tests/zone-update.c b/tests/zone-update.c new file mode 100644 index 0000000000000000000000000000000000000000..5553b8e6463d25d758b6cfcda8186459a6e95b43 --- /dev/null +++ b/tests/zone-update.c @@ -0,0 +1,116 @@ +/* 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 <tap/basic.h> + +#include "knot/updates/zone-update.h" +#include "zscanner/scanner.h" +#include "common/getline.h" + +static const char *zone_str = +"test. 3600 IN SOA a.ns.test. hostmaster.nic.cz. 1406641065 900 300 604800 900 \n" +"test. IN TXT \"test\"\n"; + +static const char *add_str = +"test. IN TXT \"test2\"\n"; + +static const char *del_str = +"test. IN TXT \"test\"\n"; + +static void process_rr(zs_scanner_t *scanner) +{ + // get zone to insert into + zone_contents_t *zone = scanner->data; + + // create data + knot_rrset_t *rr = knot_rrset_new(scanner->r_owner, + scanner->r_type, + scanner->r_class, NULL); + assert(rr); + + int ret = knot_rrset_add_rdata(rr, scanner->r_data, + scanner->r_data_length, + scanner->r_ttl, NULL); + assert(ret == KNOT_EOK); + + // add to zone + zone_node_t *n = NULL; + ret = zone_contents_add_rr(zone, rr, &n); + knot_rrset_free(&rr, NULL); + UNUSED(n); + assert(ret == KNOT_EOK); +} + +int main(int argc, char *argv[]) +{ + plan(5); + + knot_dname_t *apex = knot_dname_from_str("test"); + assert(apex); + zone_contents_t *zone = zone_contents_new(apex); + knot_dname_free(&apex, NULL); + assert(zone); + + changeset_t ch; + int ret = changeset_init(&ch, zone->apex->owner); + assert(ret == KNOT_EOK); + + zone_update_t update; + zone_update_init(&update, zone, &ch); + ok(update.zone == zone && update.change == &ch && update.mm.alloc, + "zone update: init"); + + // Fill zone + zs_scanner_t *sc = zs_scanner_create("test.", KNOT_CLASS_IN, 3600, process_rr, + NULL, zone); + assert(sc); + ret = zs_scanner_parse(sc, zone_str, zone_str + strlen(zone_str), true); + assert(ret == 0); + + // Check that old node is returned without changes + ok(zone->apex == zone_update_get_node(&update, zone->apex->owner), + "zone update: no change"); + + // Add RRs to add section + sc->data = ch.add; + ret = zs_scanner_parse(sc, add_str, add_str + strlen(add_str), true); + assert(ret == 0); + + // Check that apex TXT has two RRs now + const zone_node_t *synth_node = zone_update_get_node(&update, zone->apex->owner); + ok(synth_node && node_rdataset(synth_node, KNOT_RRTYPE_TXT)->rr_count == 2, + "zone update: add change"); + + // Add RRs to remove section + sc->data = ch.remove; + ret = zs_scanner_parse(sc, del_str, del_str + strlen(del_str), true); + assert(ret == 0); + + // Check that apex TXT has one RR again + synth_node = zone_update_get_node(&update, zone->apex->owner); + ok(synth_node && node_rdataset(synth_node, KNOT_RRTYPE_TXT)->rr_count == 1, + "zone update: del change"); + + zone_update_clear(&update); + ok(update.zone == NULL && update.change == NULL, "zone update: cleanup"); + + changeset_clear(&ch); + zs_scanner_free(sc); + zone_contents_deep_free(&zone); + + return 0; +}