diff --git a/samples/knot.sample.conf.in b/samples/knot.sample.conf.in index ede3db3da774b4ef30c12d077c512e3a40c8221d..b967fa37756563c7a8be5d8d354162bbf52df9f4 100644 --- a/samples/knot.sample.conf.in +++ b/samples/knot.sample.conf.in @@ -1,89 +1,57 @@ # # This is a sample of a minimal configuration file for Knot DNS. -# # For exhaustive list of all options see man 5 knot.conf or refer to user manual. # -system { - # Identity of the server (see RFC 4892). - identity on; - - # Version of the server (see RFC 4892) - version on; - - # User for running server - # May also specify user.group (e.g. knot.knot) - user knot.knot; - - # Directory for storing run-time data - # e.g. PID file and control sockets - # default: ${localstatedir}/run/knot, configured with --with-rundir - # rundir "@run_dir@"; -} - -interfaces { - all_ipv4 { - address 0.0.0.0; - port 53; - } - all_ipv6 { - address [::]; - port 53; - } -} - -control { - # Default: knot.sock (relative to rundir) - listen-on "knot.sock"; - - # As an alternative, you can use an IPv4/v6 address and port - # Same syntax as for 'interfaces' items - # listen-on { address 127.0.0.1@5533; } - - # Specifies ACL list for remote control - # Same syntax as for ACLs in zones - # List of remotes or groups delimited by comma - # Notice: keep in mind that ACLs bear no effect with UNIX sockets - # allow server0, admins; -} - -#remotes { -# master0 { -# address 198.51.100.1@53; -# } -# slave0 { -# address 203.0.113.1@53; -# } -#} - -zones { -# This is a default directory to place slave zone files, journals etc. -# default: ${localstatedir}/lib/knot, configured with --with-storage -# storage "@storage_dir@"; -# -# Example master zone -# example.com { -# file "@config_dir@/example.com.zone"; -# xfr-out slave0; -# notify-out slave0; -# } -# -# Example slave zone -# example.net { -# file "@storage_dir@/example.net.zone -# xfr-in master0; -# notify-in master0; -# } -} - -log { - syslog { - # Log info and more serious events to syslog - any info; - } - - # Log warnings, errors and criticals to stderr - stderr { - any warning; - } -} +server: + # Identity of the server. + identity: "Knot DNS" + + # Version of the server (automatic value). + version: "" + + # Listening interfaces (all configured IPv4 and IPv6 interfaces). + listen: 0.0.0.0@53 + listen: ::@53 + +remote: + - id: slave + address: 203.0.113.1@53 + + - id: master + address: 198.51.100.1@53 + +acl: + - id: acl_slave + address: 203.0.113.1 + action: xfer + + - id: acl_master + address: 198.51.100.1 + action: notify + +template: + - id: default + storage: "@storage_dir@" + +zone: + # Master zone. + - domain: example.com + file: "@storage_dir@/example.com.zone" + notify: slave + acl: acl_slave + + # Slave zone. + - domain: example.net + file: "@storage_dir@/example.net.zone" + master: master + acl: acl_master + +log: + # Log info and more serious events to syslog. + - to: syslog + any: info + + # Log warnings, errors and criticals to stderr. + - to: stderr + any: warning diff --git a/src/Makefile.am b/src/Makefile.am index 9c099938f277b2d72ada6bd20d3526c71ce242cb..d473c763300e8de8cb34b16d76827f2ce4ea208e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,9 +1,9 @@ ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4 SUBDIRS = zscanner dnssec dnstap . -lib_LTLIBRARIES = \ - libknot-int.la \ - libknot.la \ +lib_LTLIBRARIES = \ + libknot-int.la \ + libknot.la \ libknot-yparser.la noinst_LTLIBRARIES = @@ -54,9 +54,9 @@ nobase_libknot_int_la_HEADERS = \ libknot_yparser_ladir = $(includedir) nobase_libknot_yparser_la_HEADERS = \ - libknot/yparser/yparser.h \ - libknot/yparser/ypformat.h \ - libknot/yparser/ypscheme.h \ + libknot/yparser/yparser.h \ + libknot/yparser/ypformat.h \ + libknot/yparser/ypscheme.h \ libknot/yparser/yptrafo.h # dynamic: libknot internal sources @@ -85,11 +85,11 @@ libknot_int_la_SOURCES = \ $(nobase_libknot_int_la_HEADERS) libknot_yparser_la_SOURCES = \ - libknot/yparser/yparser.c \ - libknot/yparser/ypbody.c \ - libknot/yparser/ypformat.c \ - libknot/yparser/ypscheme.c \ - libknot/yparser/yptrafo.c \ + libknot/yparser/yparser.c \ + libknot/yparser/ypbody.c \ + libknot/yparser/ypformat.c \ + libknot/yparser/ypscheme.c \ + libknot/yparser/yptrafo.c \ $(nobase_libknot_yparser_la_HEADERS) # dynamic: libknot headers @@ -168,12 +168,12 @@ libknot_la_LIBADD = libknot-int.la dnssec/libdnssec.la zscanner/libzscanner.la pkgconfig_DATA = libknot.pc libknot-int.pc if !HAVE_LMDB -libknot_int_la_SOURCES += \ - libknot/internal/namedb/mdb.c \ - libknot/internal/namedb/midl.c \ - libknot/internal/namedb/lmdb.h \ +libknot_int_la_SOURCES += \ + libknot/internal/namedb/mdb.c \ + libknot/internal/namedb/midl.c \ + libknot/internal/namedb/lmdb.h \ libknot/internal/namedb/midl.h -libknot_int_la_CPPFLAGS += \ +libknot_int_la_CPPFLAGS += \ -I$(srcdir)/libknot/internal/namedb/ endif @@ -330,8 +330,8 @@ libknotd_la_CPPFLAGS = $(AM_CPPFLAGS) $(systemd_CFLAGS) libknotd_la_LDFLAGS = $(AM_LDFLAGS) $(systemd_LIBS) libknotd_la_LIBADD = libknot.la -knotd_LDADD = libknotd.la -knotc_LDADD = libknotd.la +knotd_LDADD = libknotd.la libknot-yparser.la +knotc_LDADD = libknotd.la libknot-yparser.la #################################### # Optional Knot DNS Daemon modules # diff --git a/src/knot/common/log.c b/src/knot/common/log.c index 14d91053577645304ffa1674f93d3d4dc0c2f509..ac4925d500db2de948947920ae6090502a1444d8 100644 --- a/src/knot/common/log.c +++ b/src/knot/common/log.c @@ -20,7 +20,7 @@ #include <stdlib.h> #include <unistd.h> #include <sys/time.h> -#include <time.h> // strftime +#include <time.h> #include <urcu.h> #ifdef ENABLE_SYSTEMD @@ -165,11 +165,8 @@ int log_init() { /* Setup initial state. */ int ret = KNOT_EOK; - int emask = LOG_MASK(LOG_WARNING)|LOG_MASK(LOG_ERR)|LOG_MASK(LOG_CRIT); - int imask = LOG_MASK(LOG_INFO)|LOG_MASK(LOG_NOTICE); - - /* Add debug messages. */ - emask |= LOG_MASK(LOG_DEBUG); + int emask = LOG_MASK(LOG_CRIT) | LOG_MASK(LOG_ERR) | LOG_MASK(LOG_WARNING); + int imask = LOG_MASK(LOG_NOTICE) | LOG_MASK(LOG_INFO) | LOG_MASK(LOG_DEBUG); /* Publish base log sink. */ struct log_sink *log = sink_setup(0); @@ -427,28 +424,6 @@ int log_msg_zone_str(int priority, const char *zone, const char *fmt, ...) return result; } -void hex_log(const char *data, int length) -{ - int ptr = 0; - char lbuf[512]={0}; int llen = 0; - for (; ptr < length; ptr++) { - if (ptr > 0 && ptr % 16 == 0) { - lbuf[llen] = '\0'; - log_msg(LOG_DEBUG, "%s\n", lbuf); - llen = 0; - } - int ret = snprintf(lbuf + llen, sizeof(lbuf) - llen, "0x%02x ", - (unsigned char)*(data + ptr)); - if (ret < 0 || ret >= sizeof(lbuf) - llen) { - return; - } - llen += ret; - } - if (llen > 0) { - log_msg(LOG_DEBUG, "%s\n", lbuf); - } -} - int log_update_privileges(int uid, int gid) { for (unsigned i = 0; i < s_log->file_count; ++i) { @@ -460,13 +435,28 @@ int log_update_privileges(int uid, int gid) return KNOT_EOK; } -int log_reconfigure(const list_t *logs, void *data) +static logtype_t get_logtype(const char *logname) +{ + assert(logname); + + if (strcasecmp(logname, "syslog") == 0) { + return LOGT_SYSLOG; + } else if (strcasecmp(logname, "stderr") == 0) { + return LOGT_STDERR; + } else if (strcasecmp(logname, "stdout") == 0) { + return LOGT_STDOUT; + } else { + return LOGT_FILE; + } +} + +int log_reconfigure(conf_t *conf, void *data) { // Data not used UNUSED(data); // Use defaults if no 'log' section is configured. - if (EMPTY_LIST(*logs)) { + if (conf_id_count(conf, C_LOG) == 0) { log_close(); log_init(); return KNOT_EOK; @@ -474,13 +464,16 @@ int log_reconfigure(const list_t *logs, void *data) // Find maximum log facility id unsigned files = 0; - node_t *list_node = NULL; - WALK_LIST(list_node, *logs) { - conf_log_t* log = (conf_log_t*)list_node; - if (log->type == LOGT_FILE) { + conf_iter_t iter = conf_iter(conf, C_LOG); + while (iter.code == KNOT_EOK) { + conf_val_t id = conf_iter_id(conf, &iter); + if (get_logtype(conf_str(&id)) == LOGT_FILE) { ++files; } + + conf_iter_next(conf, &iter); } + conf_iter_finish(conf, &iter); // Initialize logsystem struct log_sink *log = sink_setup(files); @@ -489,49 +482,46 @@ int log_reconfigure(const list_t *logs, void *data) } // Setup logs - list_node = NULL; - WALK_LIST(list_node, *logs) { + iter = conf_iter(conf, C_LOG); + while (iter.code == KNOT_EOK) { + conf_val_t id = conf_iter_id(conf, &iter); + const char *logname = conf_str(&id); - // Calculate offset - conf_log_t* facility_conf = (conf_log_t*)list_node; - int facility = facility_conf->type; + // Get facility. + int facility = get_logtype(logname); if (facility == LOGT_FILE) { - facility = log_open_file(log, facility_conf->file); + facility = log_open_file(log, logname); if (facility < 0) { log_error("failed to open log, file '%s'", - facility_conf->file); + logname); + conf_iter_next(conf, &iter); continue; } } - // Setup sources mapping - node_t *m = 0; - WALK_LIST(m, facility_conf->map) { + conf_val_t level_val; + unsigned level; - // Assign mapped level - conf_log_map_t *map = (conf_log_map_t*)m; - sink_levels_add(log, facility, map->source, map->prios); - } - } + // Set SERVER logging. + level_val = conf_id_get(conf, C_LOG, C_SERVER, &id); + level = conf_opt(&level_val); + sink_levels_add(log, facility, LOG_SERVER, level); - sink_publish(log); + // Set ZONE logging. + level_val = conf_id_get(conf, C_LOG, C_ZONE, &id); + level = conf_opt(&level_val); + sink_levels_add(log, facility, LOG_ZONE, level); - return KNOT_EOK; -} + // Set ANY logging. + level_val = conf_id_get(conf, C_LOG, C_ANY, &id); + level = conf_opt(&level_val); + sink_levels_add(log, facility, LOG_ANY, level); -void log_free(conf_log_t *log) -{ - if (!log) { - return; + conf_iter_next(conf, &iter); } + conf_iter_finish(conf, &iter); - free(log->file); - - /* Free loglevel mapping. */ - node_t *n = NULL, *nxt = NULL; - WALK_LIST_DELSAFE(n, nxt, log->map) { - free((conf_log_map_t*)n); - } + sink_publish(log); - free(log); + return KNOT_EOK; } diff --git a/src/knot/common/log.h b/src/knot/common/log.h index 440e39416bc3daa58599fdd4f8f3e3814eb1058b..00c1406fc2e4f79ed1d0b8cda5053fc097eeeb3d 100644 --- a/src/knot/common/log.h +++ b/src/knot/common/log.h @@ -39,9 +39,8 @@ #include <stdbool.h> #include <assert.h> -#include "libknot/internal/lists.h" - #include "libknot/dname.h" +#include "knot/conf/conf.h" /*! \brief Log facility types. */ typedef enum { @@ -61,25 +60,6 @@ typedef enum { LOG_ANY = 7 /*!< Any module. */ } logsrc_t; -/*! - * \brief Mapping of loglevels to message sources. - */ -typedef struct conf_log_map { - node_t n; - int source; /*!< Log message source mask. */ - int prios; /*!< Log priorities mask. */ -} conf_log_map_t; - -/*! - * \brief Log facility descriptor. - */ -typedef struct conf_log { - node_t n; - logtype_t type; /*!< Type of the log (SYSLOG/STDERR/FILE). */ - char *file; /*!< Filename in case of LOG_FILE, else NULL. */ - list_t map; /*!< Log levels mapping. */ -} conf_log_t; - /*! \brief Format for timestamps in log files. */ #define KNOT_LOG_TIME_FORMAT "%Y-%m-%dT%H:%M:%S" @@ -172,8 +152,6 @@ int log_msg_zone(int priority, const knot_dname_t *zone, const char *fmt, ...) int log_msg_zone_str(int priority, const char *zone, const char *fmt, ...) __attribute__((format(printf, 3, 4))); -void hex_log(const char *data, int length); - /* Convenient logging. */ #define log_fatal(msg...) log_msg(LOG_CRIT, msg) @@ -215,9 +193,6 @@ int log_update_privileges(int uid, int gid); * \retval KNOT_EINVAL on invalid parameters. * \retval KNOT_ENOMEM out of memory error. */ -int log_reconfigure(const list_t *logs, void *data); - -/*! \brief Free log config. */ -void log_free(conf_log_t *log); +int log_reconfigure(conf_t *conf, void *data); /*! @} */ diff --git a/src/knot/ctl/knotc_main.c b/src/knot/ctl/knotc_main.c index 504dba47314e952b9a491642365a02cf21868ec5..71bec15f140152b4e72ed6a6e95805ec2f0326be 100644 --- a/src/knot/ctl/knotc_main.c +++ b/src/knot/ctl/knotc_main.c @@ -31,7 +31,6 @@ #include "libknot/libknot.h" #include "knot/common/debug.h" #include "knot/ctl/estimator.h" -#include "knot/ctl/process.h" #include "knot/ctl/remote.h" #include "knot/conf/conf.h" #include "knot/server/tcp-handler.h" @@ -40,10 +39,9 @@ /*! \brief Controller flags. */ enum knotc_flag_t { - F_NULL = 0 << 0, - F_FORCE = 1 << 0, - F_VERBOSE = 1 << 1, - F_NOCONF = 1 << 2 + F_NULL = 0, + F_FORCE = 1 << 0, + F_VERBOSE = 1 << 1 }; /*! \brief Check if flag is present. */ @@ -52,45 +50,58 @@ static inline unsigned has_flag(unsigned flags, enum knotc_flag_t f) return flags & f; } +/*! \brief Callback arguments. */ +typedef struct cmd_args { + struct sockaddr_storage *addr; + knot_tsig_key_t *key; + int argc; + char **argv; + unsigned flags; + const char *conf_db; +} cmd_args_t; + /*! \brief Callback prototype for command. */ -typedef int (*knot_cmdf_t)(int argc, char *argv[], unsigned flags); +typedef int (*knot_cmdf_t)(cmd_args_t *args); /*! \brief Command table item. */ typedef struct knot_cmd { knot_cmdf_t cb; - int need_conf; const char *name; const char *params; const char *desc; } knot_cmd_t; /* Forward decls. */ -static int cmd_stop(int argc, char *argv[], unsigned flags); -static int cmd_reload(int argc, char *argv[], unsigned flags); -static int cmd_refresh(int argc, char *argv[], unsigned flags); -static int cmd_flush(int argc, char *argv[], unsigned flags); -static int cmd_status(int argc, char *argv[], unsigned flags); -static int cmd_zonestatus(int argc, char *argv[], unsigned flags); -static int cmd_checkconf(int argc, char *argv[], unsigned flags); -static int cmd_checkzone(int argc, char *argv[], unsigned flags); -static int cmd_memstats(int argc, char *argv[], unsigned flags); -static int cmd_signzone(int argc, char *argv[], unsigned flags); +static int cmd_stop(cmd_args_t *args); +static int cmd_reload(cmd_args_t *args); +static int cmd_refresh(cmd_args_t *args); +static int cmd_flush(cmd_args_t *args); +static int cmd_status(cmd_args_t *args); +static int cmd_zonestatus(cmd_args_t *args); +static int cmd_checkconf(cmd_args_t *args); +static int cmd_checkzone(cmd_args_t *args); +static int cmd_memstats(cmd_args_t *args); +static int cmd_signzone(cmd_args_t *args); +static int cmd_import(cmd_args_t *args); +static int cmd_export(cmd_args_t *args); /*! \brief Table of remote commands. */ knot_cmd_t knot_cmd_tbl[] = { - {&cmd_stop, 0, "stop", "", "Stop server."}, - {&cmd_reload, 0, "reload", "[<zone>...]", "Reload particular zones or reload whole\n" - " configuration and changed zones."}, - {&cmd_refresh, 0, "refresh", "[<zone>...]", "Refresh slave zones. Flag '-f' forces retransfer\n" - " (zone(s) must be specified)."}, - {&cmd_flush, 0, "flush", "[<zone>...]", "Flush journal and update zone files."}, - {&cmd_status, 0, "status", "", "Check if server is running."}, - {&cmd_zonestatus, 0, "zonestatus", "[<zone>...]", "Show status of configured zones."}, - {&cmd_checkconf, 1, "checkconf", "", "Check current server configuration."}, - {&cmd_checkzone, 1, "checkzone", "[<zone>...]", "Check zones."}, - {&cmd_memstats, 1, "memstats", "[<zone>...]", "Estimate memory use for zones."}, - {&cmd_signzone, 0, "signzone", "<zone>...", "Sign zones with available DNSSEC keys."}, - {NULL, 0, NULL, NULL, NULL} + {&cmd_stop, "stop", "", "Stop server."}, + {&cmd_reload, "reload", "[<zone>...]", "Reload particular zones or reload whole\n" + " configuration and changed zones."}, + {&cmd_refresh, "refresh", "[<zone>...]", "Refresh slave zones. Flag '-f' forces retransfer\n" + " (zone(s) must be specified)."}, + {&cmd_flush, "flush", "[<zone>...]", "Flush journal and update zone files."}, + {&cmd_status, "status", "", "Check if server is running."}, + {&cmd_zonestatus, "zonestatus", "[<zone>...]", "Show status of configured zones."}, + {&cmd_checkconf, "checkconf", "", "Check current server configuration."}, + {&cmd_checkzone, "checkzone", "[<zone>...]", "Check zones."}, + {&cmd_memstats, "memstats", "[<zone>...]", "Estimate memory use for zones."}, + {&cmd_signzone, "signzone", "<zone>...", "Sign zones with available DNSSEC keys."}, + {&cmd_import, "import", "<filename>", "Import configuration database."}, + {&cmd_export, "export", "<filename>", "Export configuration database."}, + {NULL, NULL, NULL, NULL} }; /*! \brief Print help. */ @@ -99,17 +110,19 @@ void help(void) printf("Usage: %sc [parameters] <action> [action_args]\n", PACKAGE_NAME); printf("\nParameters:\n" " -c, --config <file> Select configuration file.\n" - " -s, --server <server> Remote UNIX socket/IP address\n" - " (default %s).\n" + " (default %s)\n" + " -C, --confdb <dir> Select configuration database directory.\n" + " -s, --server <server> Remote UNIX socket/IP address.\n" + " (default %s)\n" " -p, --port <port> Remote server port (only for IP).\n" - " -y, --key <[hmac:]name:key> Use key specified on the command line\n" - " (default algorithm is hmac-md5).\n" + " -y, --key <[hmac:]name:key> Use key specified on the command line.\n" + " (default algorithm is hmac-md5)\n" " -k, --keyfile <file> Use key file (as in config section 'keys').\n" " -f, --force Force operation - override some checks.\n" " -v, --verbose Verbose mode - additional runtime information.\n" " -V, --version Print %s server version.\n" " -h, --help Print help and usage.\n", - RUN_DIR "/knot.sock", PACKAGE_NAME); + CONF_DEFAULT_FILE, RUN_DIR "/knot.sock", PACKAGE_NAME); printf("\nActions:\n"); knot_cmd_t *c = knot_cmd_tbl; while (c->name != NULL) { @@ -180,19 +193,13 @@ static int cmd_remote_reply(int c) return ret; } -static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[]) +static int cmd_remote(struct sockaddr_storage *addr, knot_tsig_key_t *key, + const char *cmd, uint16_t rrt, int argc, char *argv[]) { int rc = 0; - /* 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", cmd); - return 1; - } - /* Make query. */ - knot_pkt_t *pkt = remote_query(cmd, r->key); + knot_pkt_t *pkt = remote_query(cmd, key); if (!pkt) { log_warning("failed to prepare query for '%s'", cmd); return 1; @@ -228,8 +235,9 @@ 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 (key) { + int res = remote_query_sign(pkt->wire, &pkt->size, pkt->max_size, + key); if (res != KNOT_EOK) { log_error("failed to sign the packet"); knot_pkt_free(&pkt); @@ -241,9 +249,9 @@ static int cmd_remote(const char *cmd, uint16_t rrt, int argc, char *argv[]) /* Connect to remote. */ char addr_str[SOCKADDR_STRLEN] = {0}; - sockaddr_tostr(addr_str, sizeof(addr_str), &r->addr); + sockaddr_tostr(addr_str, sizeof(addr_str), addr); - int s = net_connected_socket(SOCK_STREAM, &r->addr, &r->via, 0); + int s = net_connected_socket(SOCK_STREAM, addr, NULL, 0); if (s < 0) { log_error("failed to connect to remote host '%s'", addr_str); knot_pkt_free(&pkt); @@ -252,7 +260,8 @@ 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) { + conf_val_t val = conf_get(conf(), C_SRV, C_MAX_CONN_REPLY); + if (poll(&pfd, 1, conf_int(&val)) != 1) { log_error("failed to connect to remote host '%s'", addr_str); close(s); knot_pkt_free(&pkt); @@ -418,7 +427,8 @@ int main(int argc, char **argv) /* Parse command line arguments */ int c = 0, li = 0, rc = 0; unsigned flags = F_NULL; - const char *config_fn = conf_find_default(); + const char *config_fn = CONF_DEFAULT_FILE; + const char *config_db = NULL; /* Remote server descriptor. */ const char *r_addr = NULL; @@ -433,6 +443,7 @@ int main(int argc, char **argv) /* Long options. */ struct option opts[] = { {"config", required_argument, 0, 'c' }, + {"confdb", required_argument, 0, 'C' }, {"server", required_argument, 0, 's' }, {"port", required_argument, 0, 'p' }, {"key", required_argument, 0, 'y' }, @@ -444,11 +455,14 @@ int main(int argc, char **argv) {0, 0, 0, 0} }; - while ((c = getopt_long(argc, argv, "s:p:y:k:fc:vVh", opts, &li)) != -1) { + while ((c = getopt_long(argc, argv, "s:p:y:k:fc:C:vVh", opts, &li)) != -1) { switch (c) { case 'c': config_fn = optarg; break; + case 'C': + config_db = optarg; + break; case 's': r_addr = optarg; break; @@ -509,44 +523,60 @@ int main(int argc, char **argv) /* Command not found. */ if (!cmd->name) { - log_error("invalid command: '%s'", argv[optind]); + log_fatal("invalid command: '%s'", argv[optind]); rc = 1; goto exit; } - /* Open config, create empty if not exists. */ - if (conf_open(config_fn) != KNOT_EOK) { - s_config = conf_new(""); - flags |= F_NOCONF; - } - - /* Check if config file is required. */ - if (has_flag(flags, F_NOCONF) && cmd->need_conf) { - log_error("failed to find a config file, refusing to continue"); - rc = 1; - goto exit; - } + /* Open configuration. */ + conf_t *new_conf = NULL; + if (config_db == NULL) { + int ret = conf_new(&new_conf, conf_scheme, NULL); + if (ret != KNOT_EOK) { + log_fatal("failed to initialize configuration database " + "(%s)", knot_strerror(ret)); + rc = 1; + goto exit; + } - /* Create remote iface if not present in config. */ - conf_iface_t *ctl_if = conf()->ctl.iface; - if (!ctl_if) { - ctl_if = malloc(sizeof(conf_iface_t)); - memset(ctl_if, 0, sizeof(conf_iface_t)); - conf()->ctl.iface = ctl_if; + /* Import the configuration file. */ + ret = conf_import(new_conf, config_fn, true); + if (ret != KNOT_EOK) { + log_fatal("failed to load configuration file '%s' (%s)", + config_fn, knot_strerror(ret)); + rc = 1; + goto exit; + } - /* Fill defaults. */ - if (!r_addr) { - r_addr = RUN_DIR "/knot.sock"; - } else if (r_port < 0) { - r_port = REMOTE_DPORT; + new_conf->filename = strdup(config_fn); + } else { + /* Open configuration database. */ + int ret = conf_new(&new_conf, conf_scheme, config_db); + if (ret != KNOT_EOK) { + log_fatal("failed to open configuration database '%s' " + "(%s)", config_db, knot_strerror(ret)); + rc = 1; + goto exit; } } - /* Install the key. */ - if (r_key.name) { - ctl_if->key = &r_key; + /* Run post-open config operations. */ + int res = conf_post_open(new_conf); + if (res != KNOT_EOK) { + log_fatal("failed to use configuration (%s)", knot_strerror(res)); + conf_free(new_conf, false); + return EXIT_FAILURE; } + conf_update(new_conf); + + /* Get control address. */ + conf_val_t listen_val = conf_get(conf(), C_CTL, C_LISTEN); + conf_val_t rundir_val = conf_get(conf(), C_SRV, C_RUNDIR); + char *rundir = conf_abs_path(&rundir_val, NULL); + struct sockaddr_storage addr = conf_addr(&listen_val, rundir); + free(rundir); + /* Override from command line. */ if (r_addr) { /* Check for v6 address. */ @@ -563,11 +593,11 @@ int main(int argc, char **argv) family = AF_UNIX; } - sockaddr_set(&ctl_if->addr, family, r_addr, sockaddr_port(&ctl_if->addr)); + sockaddr_set(&addr, family, r_addr, sockaddr_port(&addr)); } if (r_port > 0) { - sockaddr_port_set(&ctl_if->addr, r_port); + sockaddr_port_set(&addr, r_port); } /* Verbose mode. */ @@ -576,102 +606,132 @@ int main(int argc, char **argv) LOG_MASK(LOG_INFO) | LOG_MASK(LOG_DEBUG)); } + cmd_args_t args = { + &addr, + r_key.name != NULL ? &r_key : NULL, + argc - optind - 1, + argv + optind + 1, + flags, + config_db + }; + /* Execute command. */ dnssec_crypto_init(); - rc = cmd->cb(argc - optind - 1, argv + optind + 1, flags); + rc = cmd->cb(&args); dnssec_crypto_cleanup(); exit: /* Finish */ knot_tsig_key_free(&r_key); + conf_free(conf(), false); log_close(); return rc; } -static int cmd_stop(int argc, char *argv[], unsigned flags) +static int cmd_stop(cmd_args_t *args) { - UNUSED(argc); - UNUSED(argv); - UNUSED(flags); - - if (argc > 0) { + if (args->argc > 0) { printf("command does not take arguments\n"); return KNOT_EINVAL; } - return cmd_remote("stop", KNOT_RRTYPE_TXT, 0, NULL); + return cmd_remote(args->addr, args->key, "stop", KNOT_RRTYPE_TXT, + 0, NULL); +} + +static int cmd_reload(cmd_args_t *args) +{ + return cmd_remote(args->addr, args->key, "reload", KNOT_RRTYPE_NS, + args->argc, args->argv); } -static int cmd_reload(int argc, char *argv[], unsigned flags) +static int cmd_refresh(cmd_args_t *args) { - UNUSED(flags); + const char *action = (args->flags & F_FORCE) ? "retransfer" : "refresh"; - return cmd_remote("reload", KNOT_RRTYPE_NS, argc, argv); + return cmd_remote(args->addr, args->key, action, KNOT_RRTYPE_NS, + args->argc, args->argv); } -static int cmd_refresh(int argc, char *argv[], unsigned flags) +static int cmd_flush(cmd_args_t *args) { - UNUSED(flags); + return cmd_remote(args->addr, args->key, "flush", KNOT_RRTYPE_NS, + args->argc, args->argv); +} - if (flags & F_FORCE) { - return cmd_remote("retransfer", KNOT_RRTYPE_NS, argc, argv); - } else { - return cmd_remote("refresh", KNOT_RRTYPE_NS, argc, argv); +static int cmd_status(cmd_args_t *args) +{ + if (args->argc > 0) { + printf("command does not take arguments\n"); + return KNOT_EINVAL; } + + return cmd_remote(args->addr, args->key, "status", KNOT_RRTYPE_TXT, + 0, NULL); } -static int cmd_flush(int argc, char *argv[], unsigned flags) +static int cmd_zonestatus(cmd_args_t *args) { - UNUSED(flags); + return cmd_remote(args->addr, args->key, "zonestatus", KNOT_RRTYPE_NS, + args->argc, args->argv); +} - return cmd_remote("flush", KNOT_RRTYPE_NS, argc, argv); +static int cmd_signzone(cmd_args_t *args) +{ + return cmd_remote(args->addr, args->key, "signzone", KNOT_RRTYPE_NS, + args->argc, args->argv); } -static int cmd_status(int argc, char *argv[], unsigned flags) +static int cmd_import(cmd_args_t *args) { - UNUSED(argv); - UNUSED(flags); + if (args->argc != 1) { + printf("command takes one argument\n"); + return KNOT_EINVAL; + } - if (argc > 0) { - printf("command does not take arguments\n"); + if (!(args->flags & F_FORCE)) { + printf("use force option to import/replace config DB\n"); return KNOT_EINVAL; } - return cmd_remote("status", KNOT_RRTYPE_TXT, 0, NULL); -} + conf_t *new_conf = NULL; + int ret = conf_new(&new_conf, conf_scheme, args->conf_db); + if (ret != KNOT_EOK) { + printf("failed to open configuration (%s)", args->conf_db); + return ret; + } -static int cmd_zonestatus(int argc, char *argv[], unsigned flags) -{ - UNUSED(flags); + ret = conf_import(new_conf, args->argv[0], true); + if (ret != KNOT_EOK) { + printf("failed to import configuration (%s)", args->argv[0]); + conf_free(new_conf, false); + return ret; + } - return cmd_remote("zonestatus", KNOT_RRTYPE_NS, argc, argv); + return KNOT_EOK; } -static int cmd_signzone(int argc, char *argv[], unsigned flags) +static int cmd_export(cmd_args_t *args) { - return cmd_remote("signzone", KNOT_RRTYPE_NS, argc, argv); + if (args->argc != 1) { + printf("command takes one argument\n"); + return KNOT_EINVAL; + } + + return conf_export(conf(), args->argv[0], YP_SNONE); } -static int cmd_checkconf(int argc, char *argv[], unsigned flags) +static int cmd_checkconf(cmd_args_t *args) { - UNUSED(argc); - UNUSED(argv); - UNUSED(flags); + UNUSED(args); log_info("configuration is valid"); return 0; } -static bool fetch_zone(int argc, char *argv[], conf_zone_t *zone) +static bool fetch_zone(int argc, char *argv[], const knot_dname_t *name) { - /* Convert zone name to dname */ - knot_dname_t *zone_name = knot_dname_from_str_alloc(zone->name); - if (zone_name == NULL) { - return false; - } - (void)knot_dname_to_lower(zone_name); - bool found = false; int i = 0; @@ -681,77 +741,61 @@ static bool fetch_zone(int argc, char *argv[], conf_zone_t *zone) if (arg_name != NULL) { (void)knot_dname_to_lower(arg_name); - found = knot_dname_is_equal(zone_name, arg_name); + found = knot_dname_is_equal(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) +static int cmd_checkzone(cmd_args_t *args) { - UNUSED(flags); - /* Zone checking */ int rc = 0; /* Generate databases for all zones */ - const bool sorted = false; - hattrie_iter_t *z_iter = hattrie_iter_begin(conf()->zones, sorted); - if (z_iter == NULL) { - return KNOT_ERROR; - } - for (; !hattrie_iter_finished(z_iter); hattrie_iter_next(z_iter)) { - conf_zone_t *zone = (conf_zone_t *)*hattrie_iter_val(z_iter); + conf_iter_t iter = conf_iter(conf(), C_ZONE); + while (iter.code == KNOT_EOK) { + conf_val_t id = conf_iter_id(conf(), &iter); /* Fetch zone */ - int zone_match = fetch_zone(argc, argv, zone); - - if (!zone_match && argc > 0) { - conf_free_zone(zone); + int zone_match = fetch_zone(args->argc, args->argv, conf_dname(&id)); + if (!zone_match && args->argc > 0) { continue; } /* Create zone loader context. */ - zone_contents_t *loaded_zone = zone_load_contents(zone); + zone_contents_t *loaded_zone = zone_load_contents(conf(), conf_dname(&id)); if (loaded_zone == NULL) { rc = 1; continue; } - log_zone_str_info(zone->name, "zone is valid"); + log_zone_info(conf_dname(&id), "zone is valid"); zone_contents_deep_free(&loaded_zone); + conf_iter_next(conf(), &iter); } - hattrie_iter_free(z_iter); + conf_iter_finish(conf(), &iter); return rc; } -static int cmd_memstats(int argc, char *argv[], unsigned flags) +static int cmd_memstats(cmd_args_t *args) { - UNUSED(flags); - /* Zone checking */ double total_size = 0; /* Generate databases for all zones */ - const bool sorted = false; - hattrie_iter_t *z_iter = hattrie_iter_begin(conf()->zones, sorted); - if (z_iter == NULL) { - return KNOT_ERROR; - } - for (; !hattrie_iter_finished(z_iter); hattrie_iter_next(z_iter)) { - conf_zone_t *zone = (conf_zone_t *)*hattrie_iter_val(z_iter); + conf_iter_t iter = conf_iter(conf(), C_ZONE); + while (iter.code == KNOT_EOK) { + conf_val_t id = conf_iter_id(conf(), &iter); /* Fetch zone */ - int zone_match = fetch_zone(argc, argv, zone); - - if (!zone_match && argc > 0) { - conf_free_zone(zone); + int zone_match = fetch_zone(args->argc, args->argv, conf_dname(&id)); + if (!zone_match && args->argc > 0) { continue; } @@ -772,24 +816,35 @@ static int cmd_memstats(int argc, char *argv[], unsigned flags) } /* Create zone scanner. */ - zs_scanner_t *zs = zs_scanner_create(zone->name, + char *zone_name = knot_dname_to_str_alloc(conf_dname(&id)); + if (zone_name == NULL) { + log_error("not enough memory"); + hattrie_free(est.node_table); + break; + } + zs_scanner_t *zs = zs_scanner_create(zone_name, KNOT_CLASS_IN, 3600, estimator_rrset_memsize_wrap, process_error, &est); + free(zone_name); if (zs == NULL) { - log_zone_str_error(zone->name, "failed to load zone"); + log_zone_error(conf_dname(&id), "failed to load zone"); hattrie_free(est.node_table); + conf_iter_next(conf(), &iter); continue; } /* Do a parser run, but do not actually create the zone. */ - int ret = zs_scanner_parse_file(zs, zone->file); + char *zonefile = conf_zonefile(conf(), conf_dname(&id)); + int ret = zs_scanner_parse_file(zs, zonefile); + free(zonefile); if (ret != 0) { - log_zone_str_error(zone->name, "failed to parse zone"); + log_zone_error(conf_dname(&id), "failed to parse zone"); hattrie_apply_rev(est.node_table, estimator_free_trie_node, NULL); hattrie_free(est.node_table); zs_scanner_free(zs); + conf_iter_next(conf(), &iter); continue; } @@ -807,15 +862,15 @@ 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", - est.record_count, (size_t)zone_size); + log_zone_info(conf_dname(&id), "%zu RRs, used memory estimation is %zu MB", + est.record_count, (size_t)zone_size); zs_scanner_free(zs); total_size += zone_size; - conf_free_zone(zone); + conf_iter_next(conf(), &iter); } - hattrie_iter_free(z_iter); + conf_iter_finish(conf(), &iter); - if (argc == 0) { // for all zones + if (args->argc == 0) { // for all zones log_info("estimated memory consumption for all zones is %zu MB", (size_t)total_size); } diff --git a/src/knot/ctl/process.c b/src/knot/ctl/process.c index 3b05e7c1443292033580724f4e420dab8e453d12..8b99fe7f967dc7da8c9d3e23526dbc3cea9196fe 100644 --- a/src/knot/ctl/process.c +++ b/src/knot/ctl/process.c @@ -21,36 +21,38 @@ #include <string.h> #include <signal.h> #include <grp.h> -#include <unistd.h> #include <assert.h> #include <sys/wait.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/types.h> #include <pwd.h> +#include <urcu.h> -#include "libknot/internal/mem.h" -#include "libknot/libknot.h" #include "knot/ctl/process.h" #include "knot/conf/conf.h" +#include "knot/common/log.h" +#include "libknot/errcode.h" +#include "libknot/internal/mem.h" +#include "libknot/libknot.h" + +#define LOCK_FILE "knot.lock" char* pid_filename() { rcu_read_lock(); - - /* Read configuration. */ - char* ret = NULL; - - if (conf()) { - if (conf()->pidfile != NULL) - ret = strdup(conf()->pidfile); - else if (conf()->rundir != NULL) - ret = strcdup(conf()->rundir, "/knot.pid"); - } - + conf_val_t val = conf_get(conf(), C_SRV, C_RUNDIR); + char *rundir = conf_abs_path(&val, NULL); + val = conf_get(conf(), C_SRV, C_PIDFILE); + char *pidfile = conf_abs_path(&val, rundir); rcu_read_unlock(); - return ret; + if (rundir != NULL) { + free(rundir); + return pidfile; + } else { + return NULL; + } } pid_t pid_read(const char* fn) @@ -171,35 +173,39 @@ int proc_update_privileges(int uid, int gid) } } - /* Check storage writeability. */ - int ret = KNOT_EOK; - const bool sorted = false; - hattrie_iter_t *z_iter = hattrie_iter_begin(conf()->zones, sorted); - if (z_iter == NULL) { - return KNOT_ERROR; - } - for (; !hattrie_iter_finished(z_iter); hattrie_iter_next(z_iter)) { - conf_zone_t *zone = (conf_zone_t *)*hattrie_iter_val(z_iter); - char *lfile = strcdup(zone->storage, "/knot.lock"); + /* Check storage writability. */ + conf_iter_t iter = conf_iter(conf(), C_ZONE); + while (iter.code == KNOT_EOK) { + conf_val_t id = conf_iter_id(conf(), &iter); + conf_val_t val = conf_zone_get(conf(), C_STORAGE, conf_dname(&id)); + char *storage = conf_abs_path(&val, NULL); + if (storage == NULL) { + conf_iter_finish(conf(), &iter); + return KNOT_ENOMEM; + } + + char *lfile = sprintf_alloc("%s/%s", storage, LOCK_FILE); assert(lfile != NULL); - FILE* fp = fopen(lfile, "w"); + FILE *fp = fopen(lfile, "w"); if (fp == NULL) { - log_warning("storage directory '%s' is not writable", - zone->storage); - ret = KNOT_EACCES; + log_error("storage directory '%s' is not writable", + storage); + free(lfile); + free(storage); + conf_iter_finish(conf(), &iter); + return KNOT_EACCES; } else { fclose(fp); unlink(lfile); } free(lfile); + free(storage); - if (ret != KNOT_EOK) { - break; - } + conf_iter_next(conf(), &iter); } - hattrie_iter_free(z_iter); + conf_iter_finish(conf(), &iter); - return ret; + return KNOT_EOK; } char *pid_check_and_create() diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c index b4d7b0a86c79eec15e0309aea6382e24f254b9c4..ad4cef1d54ccbda974c129d4ee1d312046327f0a 100644 --- a/src/knot/ctl/remote.c +++ b/src/knot/ctl/remote.c @@ -16,6 +16,7 @@ #include <assert.h> #include <sys/stat.h> +#include <urcu.h> #include "dnssec/random.h" #include "knot/common/debug.h" @@ -172,7 +173,11 @@ static int remote_zone_refresh(zone_t *zone, remote_cmdargs_t *a) { UNUSED(a); - if (zone_master(zone) == NULL) { + rcu_read_lock(); + bool is_master = zone_is_master(zone); + rcu_read_unlock(); + + if (is_master) { return KNOT_EINVAL; } @@ -194,7 +199,11 @@ static int remote_zone_retransfer(zone_t *zone, remote_cmdargs_t *a) { UNUSED(a); - if (zone_master(zone) == NULL) { + rcu_read_lock(); + bool is_master = zone_is_master(zone); + rcu_read_unlock(); + + if (is_master) { return KNOT_EINVAL; } @@ -221,7 +230,12 @@ static int remote_zone_sign(zone_t *zone, remote_cmdargs_t *a) { UNUSED(a); - if (zone == NULL || !zone->conf->dnssec_enable) { + rcu_read_lock(); + conf_val_t val = conf_zone_get(conf(), C_DNSSEC_ENABLE, zone->name); + bool dnssec_enable = conf_bool(&val); + rcu_read_unlock(); + + if (zone == NULL || !dnssec_enable) { return KNOT_EINVAL; } @@ -342,15 +356,22 @@ static int remote_zonestatus(zone_t *zone, remote_cmdargs_t *a) /* Prepare zone info. */ char buf[512] = { '\0' }; char dnssec_buf[128] = { '\0' }; + char *zone_name = knot_dname_to_str_alloc(zone->name); + + conf_val_t val = conf_zone_get(conf(), C_DNSSEC_ENABLE, zone->name); + bool dnssec_enable = conf_bool(&val); + bool is_master = zone_is_master(zone); + int n = snprintf(buf, sizeof(buf), "%s\ttype=%s | serial=%u | %s %s | %s %s\n", - zone->conf->name, - zone_master(zone) ? "slave" : "master", + zone_name, + is_master ? "master" : "slave", serial, next_name, when, - zone->conf->dnssec_enable ? "automatic DNSSEC, resigning at:" : "DNSSEC signing disabled", - zone->conf->dnssec_enable ? dnssec_info(zone, dnssec_buf, sizeof(dnssec_buf)) : ""); + dnssec_enable ? "automatic DNSSEC, resigning at:" : "DNSSEC signing disabled", + dnssec_enable ? dnssec_info(zone, dnssec_buf, sizeof(dnssec_buf)) : ""); + free(zone_name); if (n < 0 || n >= sizeof(buf)) { return KNOT_ESPACE; } @@ -495,21 +516,21 @@ static int remote_senderr(int c, uint8_t *qbuf, size_t buflen) /* Public APIs. */ -int remote_bind(conf_iface_t *desc) +int remote_bind(struct sockaddr_storage *addr) { - if (desc == NULL) { + if (addr == NULL) { return KNOT_EINVAL; } - char addr_str[SOCKADDR_STRLEN] = {0}; - sockaddr_tostr(addr_str, sizeof(addr_str), &desc->addr); + char addr_str[SOCKADDR_STRLEN] = { 0 }; + sockaddr_tostr(addr_str, sizeof(addr_str), addr); log_info("remote control, binding to '%s'", addr_str); /* Create new socket. */ mode_t old_umask = umask(KNOT_CTL_SOCKET_UMASK); - int sock = net_bound_socket(SOCK_STREAM, &desc->addr, 0); + int sock = net_bound_socket(SOCK_STREAM, addr, 0); if (sock == KNOT_EADDRNOTAVAIL) { - sock = net_bound_socket(SOCK_STREAM, &desc->addr, NET_BIND_NONLOCAL); + sock = net_bound_socket(SOCK_STREAM, addr, NET_BIND_NONLOCAL); if (sock >= 0) { log_warning("remote control, address '%s' is not available", addr_str); @@ -533,16 +554,16 @@ int remote_bind(conf_iface_t *desc) return sock; } -int remote_unbind(conf_iface_t *desc, int sock) +int remote_unbind(struct sockaddr_storage *addr, int sock) { - if (desc == NULL || sock < 0) { + if (addr == NULL || sock < 0) { return KNOT_EINVAL; } /* Remove control socket file. */ - if (desc->addr.ss_family == AF_UNIX) { - char addr_str[SOCKADDR_STRLEN] = {0}; - sockaddr_tostr(addr_str, sizeof(addr_str), &desc->addr); + if (addr->ss_family == AF_UNIX) { + char addr_str[SOCKADDR_STRLEN] = { 0 }; + sockaddr_tostr(addr_str, sizeof(addr_str), addr); unlink(addr_str); } @@ -852,7 +873,7 @@ static int zones_verify_tsig_query(const knot_pkt_t *query, return ret; } -int remote_process(server_t *s, conf_iface_t *ctl_if, int sock, +int remote_process(server_t *s, struct sockaddr_storage *ctl_addr, int sock, uint8_t* buf, size_t buflen) { knot_pkt_t *pkt = knot_pkt_new(buf, buflen, NULL); @@ -876,40 +897,38 @@ int remote_process(server_t *s, conf_iface_t *ctl_if, int sock, /* Parse packet and answer if OK. */ int ret = remote_parse(pkt); - if (ret == KNOT_EOK && ctl_if->addr.ss_family != AF_UNIX) { - - /* Check ACL list. */ - char addr_str[SOCKADDR_STRLEN] = {0}; + if (ret == KNOT_EOK && ctl_addr->ss_family != AF_UNIX) { + char addr_str[SOCKADDR_STRLEN] = { 0 }; sockaddr_tostr(addr_str, sizeof(addr_str), &ss); - knot_tsig_key_t *tsig_key = NULL; - const knot_dname_t *tsig_name = NULL; + + /* Prepare tsig parameters. */ + knot_tsig_key_t tsig = { NULL }; if (pkt->tsig_rr) { - tsig_name = pkt->tsig_rr->owner; + tsig.name = pkt->tsig_rr->owner; + tsig.algorithm = knot_tsig_rdata_alg(pkt->tsig_rr); } - conf_iface_t *match = acl_find(&conf()->ctl.allow, &ss, tsig_name); - uint16_t ts_rc = 0; - uint16_t ts_trc = 0; - uint64_t ts_tmsigned = 0; - if (match == NULL) { + + /* Check ACL. */ + rcu_read_lock(); + conf_val_t acl = conf_get(conf(), C_CTL, C_ACL); + bool allowed = acl_allowed(&acl, ACL_ACTION_CNTL, &ss, &tsig); + rcu_read_unlock(); + + if (!allowed) { log_warning("remote control, denied '%s', " "no matching ACL", addr_str); remote_senderr(client, pkt->wire, pkt->size); ret = KNOT_EACCES; goto finish; - } else { - tsig_key = match->key; } /* Check TSIG. */ - if (tsig_key) { - if (pkt->tsig_rr == NULL) { - log_warning("remote control, denied '%s', " - "key required", addr_str); - remote_senderr(client, pkt->wire, pkt->size); - ret = KNOT_EACCES; - goto finish; - } - ret = zones_verify_tsig_query(pkt, tsig_key, &ts_rc, + if (tsig.name != NULL) { + uint16_t ts_rc = 0; + uint16_t ts_trc = 0; + uint64_t ts_tmsigned = 0; + + ret = zones_verify_tsig_query(pkt, &tsig, &ts_rc, &ts_trc, &ts_tmsigned); if (ret != KNOT_EOK) { log_warning("remote control, denied '%s', " diff --git a/src/knot/ctl/remote.h b/src/knot/ctl/remote.h index 19ecdc535df45bd10cd1ec875ddf76687262be7e..b0981c9fd4f9b6452ced9f27da2c55fedc94b781 100644 --- a/src/knot/ctl/remote.h +++ b/src/knot/ctl/remote.h @@ -26,37 +26,37 @@ #pragma once -#include "knot/conf/conf.h" #include "libknot/packet/pkt.h" #include "libknot/rrset.h" #include "libknot/dnssec/key.h" #include "knot/server/server.h" -/*! \brief Default remote control tool port. */ -#define REMOTE_DPORT 5533 +/*! \brief Default connection control parameters. */ +#define REMOTE_PORT 5533 +#define REMOTE_SOCKET "knot.sock" /*! - * \brief Bind RC interface according to configuration. + * \brief Bind RC interface. * - * \param desc Interface descriptor (address, port). + * \param addr Interface descriptor (address, port). * * \retval socket if passed. * \retval knot_error else. */ -int remote_bind(conf_iface_t *desc); +int remote_bind(struct sockaddr_storage *addr); /*! * \brief Unbind from RC interface and close socket. * * \note Breaks all pending connections. * - * \param desc Interface descriptor (address, port). + * \param addr Interface descriptor (address, port). * \param socket Interface socket * * \retval KNOT_EOK on success. * \retval knot_error else. */ -int remote_unbind(conf_iface_t *desc, int sock); +int remote_unbind(struct sockaddr_storage *addr, int sock); /*! * \brief Poll new events on RC socket. @@ -112,7 +112,7 @@ int remote_answer(int sock, server_t *s, knot_pkt_t *pkt); * \note This should be used as a high-level API for workers. * * \param s Server instance. - * \param ctl_if Control interface. + * \param ctl_addr Control interface address. * \param sock RC interface socket. * \param buf Buffer for commands/responses. * \param buflen Maximum buffer size. @@ -120,7 +120,7 @@ int remote_answer(int sock, server_t *s, knot_pkt_t *pkt); * \retval KNOT_EOK on success. * \retval knot_error else. */ -int remote_process(server_t *s, conf_iface_t *ctl_if, int sock, +int remote_process(server_t *s, struct sockaddr_storage *ctl_addr, int sock, uint8_t* buf, size_t buflen); /* Functions for creating RC packets. */ diff --git a/src/knot/dnssec/policy.c b/src/knot/dnssec/policy.c index ad8883e6bbb23778656eac58612fc820b6de69cb..3974c50c5fb7729f9d55e35e1410f63cbd25999b 100644 --- a/src/knot/dnssec/policy.c +++ b/src/knot/dnssec/policy.c @@ -21,9 +21,6 @@ #include "knot/zone/contents.h" #include "libknot/rrtype/soa.h" -#define MINIMAL_RRSIG_LIFETIME (3 * 60 * 60) -#define DEFAULT_RRSIG_LIFETIME (30 * 24 * 60 * 60) - static uint32_t zone_soa_min_ttl(const zone_contents_t *zone) { knot_rrset_t soa = node_rrset(zone->apex, KNOT_RRTYPE_SOA); @@ -47,16 +44,11 @@ void update_policy_from_zone(dnssec_kasp_policy_t *policy, policy->zone_maximal_ttl = 0; // TODO } -void set_default_policy(dnssec_kasp_policy_t *policy, const conf_zone_t *config, +void set_default_policy(dnssec_kasp_policy_t *policy, const zone_contents_t *zone) { - if (config->sig_lifetime <= 0) { - policy->rrsig_lifetime = DEFAULT_RRSIG_LIFETIME; - } else if (config->sig_lifetime < MINIMAL_RRSIG_LIFETIME) { - policy->rrsig_lifetime = MINIMAL_RRSIG_LIFETIME; - } else { - policy->rrsig_lifetime = config->sig_lifetime; - } + conf_val_t val = conf_zone_get(conf(), C_SIG_LIFETIME, zone->apex->owner); + policy->rrsig_lifetime = conf_int(&val); policy->rrsig_refresh_before = policy->rrsig_lifetime / 10; policy->algorithm = 0; policy->propagation_delay = 0; diff --git a/src/knot/dnssec/policy.h b/src/knot/dnssec/policy.h index 1dbec19a8e7c0bb49b25be83425b1ba766c5facc..fe336490332aee3bcf45dae4c7e020792338ae4f 100644 --- a/src/knot/dnssec/policy.h +++ b/src/knot/dnssec/policy.h @@ -29,5 +29,5 @@ void update_policy_from_zone(dnssec_kasp_policy_t *policy, /*! * \brief Set default DNSSEC policy for zone without assigned policy. */ -void set_default_policy(dnssec_kasp_policy_t *policy, const conf_zone_t *config, +void set_default_policy(dnssec_kasp_policy_t *policy, const zone_contents_t *zone); diff --git a/src/knot/dnssec/zone-events.c b/src/knot/dnssec/zone-events.c index 660908c6d2bf586aa29da495e7afb01bdadb134f..fc3aaf845cbf7f8072a851900d9f2a03968ab6e1 100644 --- a/src/knot/dnssec/zone-events.c +++ b/src/knot/dnssec/zone-events.c @@ -38,14 +38,28 @@ static bool has_policy(const kdnssec_ctx_t *ctx) return ctx && ctx->policy && ctx->policy->name; } -static int sign_init(const zone_contents_t *zone, const conf_zone_t *config, - int flags, kdnssec_ctx_t *ctx) +static int sign_init(const zone_contents_t *zone, int flags, kdnssec_ctx_t *ctx) { assert(zone); - assert(config); assert(ctx); - int r = kdnssec_ctx_init(ctx, config->dnssec_keydir, config->name); + const knot_dname_t *zone_name = zone->apex->owner; + + conf_val_t val = conf_zone_get(conf(), C_STORAGE, zone_name); + char *storage = conf_abs_path(&val, NULL); + val = conf_zone_get(conf(), C_DNSSEC_KEYDIR, zone_name); + char *keydir = conf_abs_path(&val, storage); + free(storage); + + char *zone_name_str = knot_dname_to_str_alloc(zone_name); + if (zone_name_str == NULL) { + free(keydir); + return KNOT_ENOMEM; + } + + int r = kdnssec_ctx_init(ctx, keydir, zone_name_str); + free(zone_name_str); + free(keydir); if (r != KNOT_EOK) { return r; } @@ -55,7 +69,7 @@ static int sign_init(const zone_contents_t *zone, const conf_zone_t *config, if (has_policy(ctx)) { update_policy_from_zone(ctx->policy, zone); } else { - set_default_policy(ctx->policy, config, zone); + set_default_policy(ctx->policy, zone); } ctx->policy->nsec3_enabled = knot_is_nsec3_enabled(zone); // TODO: temporary @@ -70,7 +84,8 @@ static int sign_init(const zone_contents_t *zone, const conf_zone_t *config, if (flags & ZONE_SIGN_KEEP_SOA_SERIAL) { ctx->new_serial = ctx->old_serial; } else { - ctx->new_serial = serial_next(ctx->old_serial, config->serial_policy); + val = conf_zone_get(conf(), C_SERIAL_POLICY, zone_name); + ctx->new_serial = serial_next(ctx->old_serial, conf_opt(&val)); } return KNOT_EOK; @@ -162,11 +177,10 @@ static uint32_t schedule_next(kdnssec_ctx_t *kctx, const zone_keyset_t *keyset, return next; } -int knot_dnssec_zone_sign(zone_contents_t *zone, const conf_zone_t *config, - changeset_t *out_ch, zone_sign_flags_t flags, - uint32_t *refresh_at) +int knot_dnssec_zone_sign(zone_contents_t *zone, changeset_t *out_ch, + zone_sign_flags_t flags, uint32_t *refresh_at) { - if (!zone || !config || !out_ch || !refresh_at) { + if (!zone || !out_ch || !refresh_at) { return KNOT_EINVAL; } @@ -177,7 +191,7 @@ int knot_dnssec_zone_sign(zone_contents_t *zone, const conf_zone_t *config, // signing pipeline - result = sign_init(zone, config, flags, &ctx); + result = sign_init(zone, flags, &ctx); if (result != KNOT_EOK) { log_zone_error(zone_name, "DNSSEC, failed to initialize (%s)", knot_strerror(result)); @@ -245,7 +259,6 @@ done: } int knot_dnssec_sign_changeset(const zone_contents_t *zone, - conf_zone_t *config, const changeset_t *in_ch, changeset_t *out_ch, uint32_t *refresh_at) @@ -261,7 +274,7 @@ int knot_dnssec_sign_changeset(const zone_contents_t *zone, // signing pipeline - result = sign_init(zone, config, ZONE_SIGN_KEEP_SOA_SERIAL, &ctx); + result = sign_init(zone, ZONE_SIGN_KEEP_SOA_SERIAL, &ctx); if (result != KNOT_EOK) { log_zone_error(zone_name, "DNSSEC, failed to initialize (%s)", knot_strerror(result)); diff --git a/src/knot/dnssec/zone-events.h b/src/knot/dnssec/zone-events.h index 5f2445c75673dc22f33cf834095cdcd6852290c5..e163eb8be8342c690fa1481c270fb0bcc895b8b6 100644 --- a/src/knot/dnssec/zone-events.h +++ b/src/knot/dnssec/zone-events.h @@ -44,22 +44,19 @@ typedef enum zone_sign_flags zone_sign_flags_t; * and NSEC(3) records will not be changed. * * \param zone Zone contents to be signed. - * \param zone_config Zone/DNSSEC configuration. * \param out_ch New records will be added to this changeset. * \param flags Zone signing flags. * \param refresh_at Signature refresh time of the oldest signature in zone. * * \return Error code, KNOT_EOK if successful. */ -int knot_dnssec_zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, - changeset_t *out_ch, zone_sign_flags_t flags, - uint32_t *refresh_at); +int knot_dnssec_zone_sign(zone_contents_t *zone, changeset_t *out_ch, + zone_sign_flags_t flags, uint32_t *refresh_at); /*! * \brief Sign changeset created by DDNS or zone-diff. * * \param zone Zone contents to be signed. - * \param zone_config Zone/DNSSEC configuration. * \param in_ch Changeset created bvy DDNS or zone-diff * \param out_ch New records will be added to this changeset. * \param refresh_at Signature refresh time of the new signatures. @@ -67,7 +64,6 @@ int knot_dnssec_zone_sign(zone_contents_t *zone, const conf_zone_t *zone_config, * \return Error code, KNOT_EOK if successful. */ int knot_dnssec_sign_changeset(const zone_contents_t *zone, - conf_zone_t *zone_config, const changeset_t *in_ch, changeset_t *out_ch, uint32_t *refresh_at); diff --git a/src/knot/main.c b/src/knot/main.c index 8bbcac1742a461cabcc67d0846edfc352f72e741..44a72b1e757a9f0d7703f067f80652a16ce59af4 100644 --- a/src/knot/main.c +++ b/src/knot/main.c @@ -14,13 +14,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <dirent.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <getopt.h> #include <sys/stat.h> -#include <limits.h> +#include <urcu.h> #ifdef HAVE_CAP_NG_H #include <cap-ng.h> @@ -35,6 +36,7 @@ #include "knot/ctl/process.h" #include "knot/ctl/remote.h" #include "knot/conf/conf.h" +#include "knot/common/log.h" #include "knot/server/server.h" #include "knot/server/tcp-handler.h" #include "knot/zone/timers.h" @@ -142,7 +144,19 @@ static void event_loop(server_t *server) /* Bind to control interface. */ uint8_t buf[KNOT_WIRE_MAX_PKTSIZE]; size_t buflen = sizeof(buf); - int remote = remote_bind(conf()->ctl.iface); + + conf_val_t listen_val = conf_get(conf(), C_CTL, C_LISTEN); + conf_val_t rundir_val = conf_get(conf(), C_SRV, C_RUNDIR); + char *rundir = conf_abs_path(&rundir_val, NULL); + struct sockaddr_storage addr = conf_addr(&listen_val, rundir); + free(rundir); + + int remote = remote_bind(&addr); + if (remote < 0) { + log_fatal("failed to bind control socket (%s)", + knot_strerror(remote)); + return; + } sigset_t empty; sigemptyset(&empty); @@ -153,8 +167,7 @@ static void event_loop(server_t *server) /* Events. */ if (ret > 0) { - ret = remote_process(server, conf()->ctl.iface, - remote, buf, buflen); + ret = remote_process(server, &addr, remote, buf, buflen); if (ret == KNOT_CTL_STOP) { break; } @@ -173,7 +186,7 @@ static void event_loop(server_t *server) server_stop(server); /* Close remote control interface. */ - remote_unbind(conf()->ctl.iface, remote); + remote_unbind(&addr, remote); /* Wait for server to finish. */ server_wait(server); @@ -185,9 +198,12 @@ static void help(void) PACKAGE_NAME); printf("\nParameters:\n" " -c, --config <file> Select configuration file.\n" + " (default %s)\n" + " -C, --confdb <dir> Select configuration database directory.\n" " -d, --daemonize=[dir] Run server as a daemon.\n" " -V, --version Print version of the server.\n" - " -h, --help Print help and usage.\n"); + " -h, --help Print help and usage.\n", + CONF_DEFAULT_FILE); } int main(int argc, char **argv) @@ -195,24 +211,28 @@ int main(int argc, char **argv) /* Parse command line arguments. */ int c = 0, li = 0; int daemonize = 0; - const char *config_fn = conf_find_default(); + const char *config_fn = CONF_DEFAULT_FILE; + const char *config_db = NULL; const char *daemon_root = "/"; /* Long options. */ struct option opts[] = { - {"config", required_argument, 0, 'c'}, + {"config", required_argument, 0, 'c' }, + {"confdb", required_argument, 0, 'C' }, {"daemonize", optional_argument, 0, 'd'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; - while ((c = getopt_long(argc, argv, "c:dVh", opts, &li)) != -1) { - switch (c) - { + while ((c = getopt_long(argc, argv, "c:C:dVh", opts, &li)) != -1) { + switch (c) { case 'c': config_fn = optarg; break; + case 'C': + config_db = optarg; + break; case 'd': daemonize = 1; if (optarg) { @@ -266,41 +286,70 @@ int main(int argc, char **argv) log_init(); /* Open configuration. */ - int res = conf_open(config_fn); - conf_t *config = conf(); + conf_t *new_conf = NULL; + if (config_db == NULL) { + int ret = conf_new(&new_conf, conf_scheme, NULL); + if (ret != KNOT_EOK) { + log_fatal("failed to initialize configuration database " + "(%s)", knot_strerror(ret)); + return EXIT_FAILURE; + } + + /* Import the configuration file. */ + ret = conf_import(new_conf, config_fn, true); + if (ret != KNOT_EOK) { + log_fatal("failed to load configuration file '%s' (%s)", + config_fn, knot_strerror(ret)); + conf_free(new_conf, false); + return EXIT_FAILURE; + } + + new_conf->filename = strdup(config_fn); + } else { + /* Open configuration database. */ + int ret = conf_new(&new_conf, conf_scheme, config_db); + if (ret != KNOT_EOK) { + log_fatal("failed to open configuration database '%s' " + "(%s)", config_db, knot_strerror(ret)); + return EXIT_FAILURE; + } + } + + /* Run post-open config operations. */ + int res = conf_post_open(new_conf); if (res != KNOT_EOK) { - log_fatal("failed to load configuration file '%s' (%s)", - config_fn, knot_strerror(res)); + log_fatal("failed to use configuration (%s)", knot_strerror(res)); + conf_free(new_conf, false); return EXIT_FAILURE; } - /* Initialize logging subsystem. - * @note We're logging since now. */ - conf_log_reconfigure(config, NULL); - conf_add_hook(config, CONF_LOG, conf_log_reconfigure, NULL); + conf_update(new_conf); + + /* Initialize logging subsystem. */ + log_reconfigure(conf(), NULL); /* Initialize server. */ server_t server; - res = server_init(&server, conf_bg_threads(config)); + res = server_init(&server, conf_bg_threads(conf())); if (res != KNOT_EOK) { log_fatal("failed to initialize server (%s)", knot_strerror(res)); - conf_free(conf()); + conf_free(conf(), false); log_close(); return EXIT_FAILURE; } /* Reconfigure server interfaces. * @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", - list_size(&config->ifaces), hattrie_weight(config->zones)); + server_reconfigure(conf(), &server); + log_info("configured %zu zones", conf_id_count(conf(), C_ZONE)); /* Alter privileges. */ - log_update_privileges(config->uid, config->gid); - if (proc_update_privileges(config->uid, config->gid) != KNOT_EOK) { + int uid, gid; + conf_user(conf(), &uid, &gid); + log_update_privileges(uid, gid); + if (proc_update_privileges(uid, gid) != KNOT_EOK) { server_deinit(&server); - conf_free(conf()); + conf_free(conf(), false); log_close(); return EXIT_FAILURE; } @@ -312,7 +361,7 @@ int main(int argc, char **argv) pidfile = pid_check_and_create(); if (pidfile == NULL) { server_deinit(&server); - conf_free(conf()); + conf_free(conf(), false); log_close(); return EXIT_FAILURE; } @@ -329,10 +378,9 @@ int main(int argc, char **argv) /* Now we're going multithreaded. */ rcu_register_thread(); - /* Populate zone database and add reconfiguration hook. */ + /* Populate zone database. */ log_info("loading zones"); - server_update_zones(config, &server); - conf_add_hook(config, CONF_ALL, server_update_zones, &server); + server_update_zones(conf(), &server); /* Check number of loaded zones. */ if (knot_zonedb_size(server.zone_db) == 0) { @@ -341,14 +389,15 @@ int main(int argc, char **argv) /* Start it up. */ log_info("starting server"); - res = server_start(&server, config->async_start); + conf_val_t async_val = conf_get(conf(), C_SRV, C_ASYNC_START); + res = server_start(&server, conf_bool(&async_val)); if (res != KNOT_EOK) { log_fatal("failed to start server (%s)", knot_strerror(res)); server_deinit(&server); rcu_unregister_thread(); pid_cleanup(pidfile); log_close(); - conf_free(conf()); + conf_free(conf(), false); return EXIT_FAILURE; } @@ -360,14 +409,13 @@ int main(int argc, char **argv) } /* Start the event loop. */ - config = NULL; /* @note Invalidate pointer, as it may change now. */ event_loop(&server); /* Teardown server and configuration. */ server_deinit(&server); /* Free configuration. */ - conf_free(conf()); + conf_free(conf(), false); /* Unhook from RCU. */ rcu_unregister_thread(); diff --git a/src/knot/nameserver/axfr.c b/src/knot/nameserver/axfr.c index 2ef6af3492b295fdfbf653ae805f36aad229c1a5..dcf59a66a1cf857a871fc2d4cacd29c4854f4f81 100644 --- a/src/knot/nameserver/axfr.c +++ b/src/knot/nameserver/axfr.c @@ -14,6 +14,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <urcu.h> + #include "knot/nameserver/axfr.h" #include "knot/nameserver/internet.h" #include "knot/nameserver/process_query.h" @@ -108,7 +110,7 @@ static int axfr_query_check(struct query_data *qdata) { /* Check valid zone, transaction security and contents. */ NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH); - NS_NEED_AUTH(&qdata->zone->conf->acl.xfr_out, qdata); + NS_NEED_AUTH(qdata, qdata->zone->name, ACL_ACTION_XFER); /* Check expiration. */ NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); @@ -240,7 +242,7 @@ int axfr_query_process(knot_pkt_t *pkt, struct query_data *qdata) } /* Reserve space for TSIG. */ - knot_pkt_reserve(pkt, knot_tsig_wire_maxsize(qdata->sign.tsig_key)); + knot_pkt_reserve(pkt, knot_tsig_wire_maxsize(&qdata->sign.tsig_key)); /* Answer current packet (or continue). */ struct axfr_proc *axfr = (struct axfr_proc *)qdata->ext; diff --git a/src/knot/nameserver/chaos.c b/src/knot/nameserver/chaos.c index 086ca8e7aff258df824dfdfd1f3091f0c1ce0d5e..2c8ff893b41f0f1af88cf70df3bd907998c3b85a 100644 --- a/src/knot/nameserver/chaos.c +++ b/src/knot/nameserver/chaos.c @@ -19,6 +19,7 @@ #include "knot/nameserver/chaos.h" #include "knot/conf/conf.h" #include "libknot/descriptor.h" +#include "libknot/errcode.h" #include "libknot/packet/pkt.h" /*! @@ -29,14 +30,24 @@ static const char *get_txt_response_string(const knot_dname_t *qname) char *qname_str = knot_dname_to_str_alloc(qname); const char *response = NULL; - /* id.server and hostname.bind should have similar meaning */ + /* id.server and hostname.bind should have similar meaning. */ if (strcasecmp("id.server.", qname_str) == 0 || strcasecmp("hostname.bind.", qname_str) == 0) { - response = conf()->identity; - /* allow both version version.{server, bind}. for compatibility */ + conf_val_t val = conf_get(conf(), C_SRV, C_IDENT); + response = conf_str(&val); + /* Empty string data (including '\0') means auto. */ + if (val.code == KNOT_EOK && val.len <= 1) { + response = conf()->hostname; + } + /* Allow both version version.{server, bind}. for compatibility. */ } else if (strcasecmp("version.server.", qname_str) == 0 || strcasecmp("version.bind.", qname_str) == 0) { - response = conf()->version; + conf_val_t val = conf_get(conf(), C_SRV, C_VERSION); + response = conf_str(&val); + /* Empty string data (including '\0') means auto. */ + if (val.code == KNOT_EOK && val.len <= 1) { + response = "Knot DNS " PACKAGE_VERSION; + } } free(qname_str); @@ -57,7 +68,7 @@ static const char *get_txt_response_string(const knot_dname_t *qname) static int create_txt_rrset(knot_rrset_t *rrset, const knot_dname_t *owner, const char *response, mm_ctx_t *mm) { - // truncate response to one TXT label + /* Truncate response to one TXT label. */ size_t response_len = strlen(response); if (response_len > KNOT_DNAME_MAXLEN) { response_len = KNOT_DNAME_MAXLEN; diff --git a/src/knot/nameserver/internet.c b/src/knot/nameserver/internet.c index 1f0c43d68f26318e6516f6f0f4a7a6e0ae954ce7..f383364a52387ec4ba024e74ae984fc443276391 100644 --- a/src/knot/nameserver/internet.c +++ b/src/knot/nameserver/internet.c @@ -24,6 +24,7 @@ #include "knot/nameserver/nsec_proofs.h" #include "knot/nameserver/process_query.h" #include "knot/nameserver/process_answer.h" +#include "knot/nameserver/query_module.h" #include "knot/zone/serial.h" #include "knot/zone/zonedb.h" @@ -187,9 +188,11 @@ static int put_answer(knot_pkt_t *pkt, uint16_t type, struct query_data *qdata) int ret = KNOT_EOK; switch (type) { case KNOT_RRTYPE_ANY: /* Append all RRSets. */ { + conf_val_t val = conf_zone_get(conf(), C_DISABLE_ANY, + qdata->zone->name); /* If ANY not allowed, set TC bit. */ if ((qdata->param->proc_flags & NS_QUERY_LIMIT_ANY) && - (qdata->zone->conf->disable_any)) { + conf_bool(&val)) { dbg_ns("%s: ANY/UDP disabled for this zone TC=1\n", __func__); knot_wire_set_tc(pkt->wire); return KNOT_ESPACE; @@ -829,10 +832,10 @@ int internet_query(knot_pkt_t *response, struct query_data *qdata) /* No applicable ACL, refuse transaction security. */ if (knot_pkt_has_tsig(qdata->query)) { /* We have been challenged... */ - NS_NEED_AUTH(&qdata->zone->conf->acl.xfr_out, qdata); + NS_NEED_AUTH(qdata, qdata->zone->name, ACL_ACTION_XFER); /* Reserve space for TSIG. */ - knot_pkt_reserve(response, knot_tsig_wire_maxsize(qdata->sign.tsig_key)); + knot_pkt_reserve(response, knot_tsig_wire_maxsize(&qdata->sign.tsig_key)); } NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); /* Expired */ @@ -841,12 +844,11 @@ int internet_query(knot_pkt_t *response, struct query_data *qdata) qdata->name = knot_pkt_qname(qdata->query); /* If the zone doesn't have a query plan, go for fast default. */ - conf_zone_t *zone_config = qdata->zone->conf; - if (zone_config->query_plan == NULL) { + if (qdata->zone->query_plan == NULL) { return default_answer(response, qdata); } - return planned_answer(zone_config->query_plan, response, qdata); + return planned_answer(qdata->zone->query_plan, response, qdata); } int internet_query_plan(struct query_plan *plan) diff --git a/src/knot/nameserver/internet.h b/src/knot/nameserver/internet.h index 91a364229f602a535a83d294c0b360f4cca33f44..a9c87be5b9041d7a24950817547a31f74294c4c5 100644 --- a/src/knot/nameserver/internet.h +++ b/src/knot/nameserver/internet.h @@ -118,8 +118,8 @@ int ns_put_rr(knot_pkt_t *pkt, const knot_rrset_t *rr, } /*! \brief Require authentication. */ -#define NS_NEED_AUTH(acl, qdata) \ - if (!process_query_acl_check((acl), (qdata))) { \ +#define NS_NEED_AUTH(qdata, zone_name, action) \ + if (!process_query_acl_check((zone_name), (action), (qdata))) { \ return KNOT_STATE_FAIL; \ } else { \ if (process_query_verify(qdata) != KNOT_EOK) { \ diff --git a/src/knot/nameserver/ixfr.c b/src/knot/nameserver/ixfr.c index 98a5be7b1a0701f22cc7b601579223632998a196..28fb3eb4402a4e751132eec867a6231dec3e375d 100644 --- a/src/knot/nameserver/ixfr.c +++ b/src/knot/nameserver/ixfr.c @@ -14,6 +14,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <urcu.h> + #include "knot/nameserver/ixfr.h" #include "knot/nameserver/axfr.h" #include "knot/nameserver/internet.h" @@ -164,7 +166,7 @@ static int ixfr_process_changeset(knot_pkt_t *pkt, const void *item, #undef IXFR_SAFE_PUT /*! \brief Loads IXFRs from journal. */ -static int ixfr_load_chsets(list_t *chgsets, zone_t *zone, +static int ixfr_load_chsets(list_t *chgsets, const zone_t *zone, const knot_rrset_t *their_soa) { assert(chgsets); @@ -178,9 +180,11 @@ static int ixfr_load_chsets(list_t *chgsets, zone_t *zone, return KNOT_EUPTODATE; } - pthread_mutex_lock(&zone->journal_lock); - ret = journal_load_changesets(zone, chgsets, serial_from, serial_to); - pthread_mutex_unlock(&zone->journal_lock); + char *path = conf_journalfile(conf(), zone->name); + pthread_mutex_lock((pthread_mutex_t *)&zone->journal_lock); + ret = journal_load_changesets(path, zone, chgsets, serial_from, serial_to); + pthread_mutex_unlock((pthread_mutex_t *)&zone->journal_lock); + free(path); if (ret != KNOT_EOK) { changesets_free(chgsets); @@ -208,7 +212,7 @@ static int ixfr_query_check(struct query_data *qdata) NS_NEED_QNAME(qdata, their_soa->owner, KNOT_RCODE_FORMERR); /* Check transcation security and zone contents. */ - NS_NEED_AUTH(&qdata->zone->conf->acl.xfr_out, qdata); + NS_NEED_AUTH(qdata, qdata->zone->name, ACL_ACTION_XFER); NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); /* Check expiration. */ return KNOT_STATE_DONE; @@ -307,7 +311,7 @@ static int ixfr_answer_soa(knot_pkt_t *pkt, struct query_data *qdata) } /* Reserve space for TSIG. */ - knot_pkt_reserve(pkt, knot_tsig_wire_maxsize(qdata->sign.tsig_key)); + knot_pkt_reserve(pkt, knot_tsig_wire_maxsize(&qdata->sign.tsig_key)); /* Guaranteed to have zone contents. */ const zone_node_t *apex = qdata->zone->contents->apex; @@ -649,7 +653,7 @@ int ixfr_query(knot_pkt_t *pkt, struct query_data *qdata) } /* Reserve space for TSIG. */ - knot_pkt_reserve(pkt, knot_tsig_wire_maxsize(qdata->sign.tsig_key)); + knot_pkt_reserve(pkt, knot_tsig_wire_maxsize(&qdata->sign.tsig_key)); /* Answer current packet (or continue). */ ret = xfr_process_list(pkt, &ixfr_process_changeset, qdata); diff --git a/src/knot/nameserver/notify.c b/src/knot/nameserver/notify.c index 9b0a1c2f868a6c9b90e6a3a9e6a0966193550b82..e2a6b2cc39bd07e5375f119a506120ad34be94b9 100644 --- a/src/knot/nameserver/notify.c +++ b/src/knot/nameserver/notify.c @@ -54,7 +54,7 @@ static int notify_check_query(struct query_data *qdata) /* Check valid zone, transaction security. */ zone_t *zone = (zone_t *)qdata->zone; NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH); - NS_NEED_AUTH(&zone->conf->acl.notify_in, qdata); + NS_NEED_AUTH(qdata, zone->name, ACL_ACTION_NOTF); return KNOT_STATE_DONE; } @@ -80,7 +80,7 @@ int notify_process_query(knot_pkt_t *pkt, struct query_data *qdata) } /* Reserve space for TSIG. */ - knot_pkt_reserve(pkt, knot_tsig_wire_maxsize(qdata->sign.tsig_key)); + knot_pkt_reserve(pkt, knot_tsig_wire_maxsize(&qdata->sign.tsig_key)); /* SOA RR in answer may be included, recover serial. */ const knot_pktsection_t *answer = knot_pkt_section(qdata->query, KNOT_ANSWER); diff --git a/src/knot/nameserver/process_answer.c b/src/knot/nameserver/process_answer.c index 4bbec51723cb8680c13b027d3b02f8a02c963ce9..a8371902843d99e4bb7223e29473c6d89ac30acd 100644 --- a/src/knot/nameserver/process_answer.c +++ b/src/knot/nameserver/process_answer.c @@ -15,6 +15,7 @@ */ #include "libknot/libknot.h" +#include "knot/common/log.h" #include "knot/nameserver/process_answer.h" #include "knot/nameserver/internet.h" #include "knot/nameserver/notify.h" diff --git a/src/knot/nameserver/process_query.c b/src/knot/nameserver/process_query.c index a9e5c3c91aeedea148b4f357f193354beedc401f..02e55c01e03329c3bf0bd7aefafae4eb9adfb480 100644 --- a/src/knot/nameserver/process_query.c +++ b/src/knot/nameserver/process_query.c @@ -3,6 +3,7 @@ #include "dnssec/tsig.h" #include "knot/nameserver/process_query.h" +#include "knot/nameserver/query_module.h" #include "knot/nameserver/chaos.h" #include "knot/nameserver/internet.h" #include "knot/nameserver/axfr.h" @@ -219,7 +220,8 @@ static int answer_edns_init(const knot_pkt_t *query, knot_pkt_t *resp, } /* Initialize OPT record. */ - int ret = knot_edns_init(&qdata->opt_rr, conf()->max_udp_payload, 0, + conf_val_t val = conf_get(conf(), C_SRV, C_MAX_UDP_PAYLOAD); + int ret = knot_edns_init(&qdata->opt_rr, conf_int(&val), 0, KNOT_EDNS_VERSION, qdata->mm); if (ret != KNOT_EOK) { return ret; @@ -236,10 +238,21 @@ static int answer_edns_init(const knot_pkt_t *query, knot_pkt_t *resp, } /* Append NSID if requested and available. */ - if (knot_edns_has_nsid(query->opt_rr) && conf()->nsid_len > 0) { + val = conf_get(conf(), C_SRV, C_NSID); + if (knot_edns_has_nsid(query->opt_rr) && val.code == KNOT_EOK) { + conf_data(&val); + const uint8_t *data = val.data; + uint16_t len = val.len; + + /* Empty data means automatic value. */ + if (val.len == 0) { + data = (uint8_t *)conf()->hostname; + len = strlen(conf()->hostname); + } + ret = knot_edns_add_option(&qdata->opt_rr, - KNOT_EDNS_OPTION_NSID, conf()->nsid_len, - (const uint8_t*)conf()->nsid, qdata->mm); + KNOT_EDNS_OPTION_NSID, len, data, + qdata->mm); if (ret != KNOT_EOK) { return ret; } @@ -317,8 +330,9 @@ static int prepare_answer(const knot_pkt_t *query, knot_pkt_t *resp, knot_layer_ if (has_limit) { resp->max_size = KNOT_WIRE_MIN_PKTSIZE; if (knot_pkt_has_edns(query)) { + conf_val_t val = conf_get(conf(), C_SRV, C_MAX_UDP_PAYLOAD); uint16_t client = knot_edns_get_payload(query->opt_rr); - uint16_t server = conf()->max_udp_payload; + uint16_t server = conf_int(&val); uint16_t transfer = MIN(client, server); resp->max_size = MAX(resp->max_size, transfer); } @@ -408,7 +422,8 @@ static int ratelimit_apply(int state, knot_pkt_t *pkt, knot_layer_t *ctx) } /* Now it is slip or drop. */ - if (rrl_slip_roll(conf()->rrl_slip)) { + conf_val_t val = conf_get(conf(), C_SRV, C_RATE_LIMIT_SLIP); + if (rrl_slip_roll(conf_int(&val))) { /* Answer slips. */ if (process_query_err(ctx, pkt) != KNOT_EOK) { return KNOT_STATE_FAIL; @@ -532,27 +547,27 @@ finish: return next_state; } -bool process_query_acl_check(list_t *acl, struct query_data *qdata) +bool process_query_acl_check(const knot_dname_t *zone_name, acl_action_t action, + struct query_data *qdata) { knot_pkt_t *query = qdata->query; const struct sockaddr_storage *query_source = qdata->param->remote; - const knot_dname_t *key_name = NULL; - dnssec_tsig_algorithm_t key_alg = DNSSEC_TSIG_UNKNOWN; + knot_tsig_key_t tsig = { NULL }; /* Skip if already checked and valid. */ - if (qdata->sign.tsig_key != NULL) { + if (qdata->sign.tsig_key.name != NULL) { return true; } /* Authenticate with NOKEY if the packet isn't signed. */ if (query->tsig_rr) { - key_name = query->tsig_rr->owner; - key_alg = knot_tsig_rdata_alg(query->tsig_rr); + tsig.name = query->tsig_rr->owner; + tsig.algorithm = knot_tsig_rdata_alg(query->tsig_rr); } - conf_iface_t *match = acl_find(acl, query_source, key_name); - /* Did not authenticate, no fitting rule found. */ - if (match == NULL || (match->key && match->key->algorithm != key_alg)) { + /* Check if authenticated. */ + conf_val_t acl = conf_zone_get(conf(), C_ACL, zone_name); + if (!acl_allowed(&acl, action, query_source, &tsig)) { dbg_ns("%s: no ACL match => NOTAUTH\n", __func__); qdata->rcode = KNOT_RCODE_NOTAUTH; qdata->rcode_tsig = KNOT_TSIG_ERR_BADKEY; @@ -560,7 +575,8 @@ bool process_query_acl_check(list_t *acl, struct query_data *qdata) } /* Remember used TSIG key. */ - qdata->sign.tsig_key = match->key; + qdata->sign.tsig_key = tsig; + return true; } @@ -582,7 +598,7 @@ int process_query_verify(struct query_data *qdata) /* Checking query. */ process_query_qname_case_restore(qdata, query); int ret = knot_tsig_server_check(query->tsig_rr, query->wire, - query->size, ctx->tsig_key); + query->size, &ctx->tsig_key); process_query_qname_case_lower(query); dbg_ns("%s: QUERY TSIG check result = %s\n", __func__, knot_strerror(ret)); @@ -628,22 +644,22 @@ int process_query_sign_response(knot_pkt_t *pkt, struct query_data *qdata) knot_sign_context_t *ctx = &qdata->sign; /* KEY provided and verified TSIG or BADTIME allows signing. */ - if (ctx->tsig_key != NULL && knot_tsig_can_sign(qdata->rcode_tsig)) { + if (ctx->tsig_key.name != NULL && knot_tsig_can_sign(qdata->rcode_tsig)) { /* Sign query response. */ - dbg_ns("%s: signing response using key %p\n", __func__, ctx->tsig_key); - size_t new_digest_len = dnssec_tsig_algorithm_size(ctx->tsig_key->algorithm); + dbg_ns("%s: signing response using key %p\n", __func__, &ctx->tsig_key); + size_t new_digest_len = dnssec_tsig_algorithm_size(ctx->tsig_key.algorithm); if (ctx->pkt_count == 0) { ret = knot_tsig_sign(pkt->wire, &pkt->size, pkt->max_size, ctx->tsig_digest, ctx->tsig_digestlen, ctx->tsig_digest, &new_digest_len, - ctx->tsig_key, qdata->rcode_tsig, + &ctx->tsig_key, qdata->rcode_tsig, ctx->tsig_time_signed); } else { ret = knot_tsig_sign_next(pkt->wire, &pkt->size, pkt->max_size, ctx->tsig_digest, ctx->tsig_digestlen, ctx->tsig_digest, &new_digest_len, - ctx->tsig_key, + &ctx->tsig_key, pkt->wire, pkt->size); } if (ret != KNOT_EOK) { diff --git a/src/knot/nameserver/process_query.h b/src/knot/nameserver/process_query.h index 429d66ff3dafe2d9d37144547a9f3ccfc2e851c6..ef691b260d80516e4598c8aff69b9ed11a5bdbba 100644 --- a/src/knot/nameserver/process_query.h +++ b/src/knot/nameserver/process_query.h @@ -113,11 +113,13 @@ struct rrsig_info { /*! * \brief Check current query against ACL. * - * \param acl - * \param qdata + * \param zone_name Current zone name. + * \param action ACL action. + * \param qdata Query data. * \return true if accepted, false if denied. */ -bool process_query_acl_check(list_t *acl, struct query_data *qdata); +bool process_query_acl_check(const knot_dname_t *zone_name, acl_action_t action, + struct query_data *qdata); /*! * \brief Verify current query transaction security and update query data. diff --git a/src/knot/nameserver/update.c b/src/knot/nameserver/update.c index 769d2cf6738847ea714fda870bc8d241ad23d1bc..208eb13a23472c1576a37cefe2f07f03720694b7 100644 --- a/src/knot/nameserver/update.c +++ b/src/knot/nameserver/update.c @@ -14,6 +14,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <urcu.h> + #include "dnssec/random.h" #include "knot/nameserver/update.h" #include "knot/nameserver/internet.h" @@ -78,13 +80,12 @@ static int sign_update(zone_t *zone, const zone_contents_t *old_contents, uint32_t refresh_at = 0; 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, ZONE_SIGN_KEEP_SOA_SERIAL, + ret = knot_dnssec_zone_sign(new_contents, sec_ch, + ZONE_SIGN_KEEP_SOA_SERIAL, &refresh_at); } else { // Sign the created changeset - ret = knot_dnssec_sign_changeset(new_contents, zone->conf, - ddns_ch, sec_ch, + ret = knot_dnssec_sign_changeset(new_contents, ddns_ch, sec_ch, &refresh_at); } if (ret != KNOT_EOK) { @@ -237,9 +238,12 @@ static int process_normal(zone_t *zone, list_t *requests) } assert(new_contents); + conf_val_t val = conf_zone_get(conf(), C_DNSSEC_ENABLE, zone->name); + bool dnssec_enable = conf_bool(&val); + // Sign the update. changeset_t sec_ch; - if (zone->conf->dnssec_enable) { + if (dnssec_enable) { ret = changeset_init(&sec_ch, zone->name); if (ret != KNOT_EOK) { set_rcodes(requests, KNOT_RCODE_SERVFAIL); @@ -262,19 +266,24 @@ static int process_normal(zone_t *zone, list_t *requests) update_rollback(&ddns_ch); update_free_zone(&new_contents); changeset_clear(&ddns_ch); - if (zone->conf->dnssec_enable) { + if (dnssec_enable) { changeset_clear(&sec_ch); } set_rcodes(requests, KNOT_RCODE_SERVFAIL); return ret; } + /* Temporarily unlock locked configuration. */ + rcu_read_unlock(); + // Switch zone contents. zone_contents_t *old_contents = zone_switch_contents(zone, new_contents); synchronize_rcu(); + rcu_read_lock(); + // Clear DNSSEC changes - if (zone->conf->dnssec_enable) { + if (dnssec_enable) { update_cleanup(&sec_ch); changeset_clear(&sec_ch); } @@ -286,7 +295,8 @@ static int process_normal(zone_t *zone, list_t *requests) changeset_clear(&ddns_ch); /* Sync zonefile immediately if configured. */ - if (zone->conf->dbsync_timeout == 0) { + val = conf_zone_get(conf(), C_ZONEFILE_SYNC, zone->name); + if (conf_int(&val) == 0) { zone_events_schedule(zone, ZONE_EVENT_FLUSH, ZONE_EVENT_NOW); } @@ -331,12 +341,20 @@ static int process_requests(zone_t *zone, list_t *requests) static int forward_request(zone_t *zone, struct knot_request *request) { + /* Ignore if DNSSEC enabled. */ + conf_val_t val = conf_zone_get(conf(), C_DNSSEC_ENABLE, zone->name); + if (conf_bool(&val)) { + log_zone_notice(zone->name, "ignoring ddns forward due to " + "enabled automatic DNSSEC signing."); + return KNOT_EOK; + } + /* Create requestor instance. */ struct knot_requestor re; knot_requestor_init(&re, NULL); /* Fetch primary master. */ - const conf_iface_t *master = zone_master(zone); + const conf_remote_t master = zone_master(zone); /* Copy request and assign new ID. */ knot_pkt_t *query = knot_pkt_new(NULL, request->query->max_size, NULL); @@ -350,8 +368,8 @@ static int forward_request(zone_t *zone, struct knot_request *request) knot_tsig_append(query->wire, &query->size, query->max_size, query->tsig_rr); /* Create a request. */ - const struct sockaddr *dst = (const struct sockaddr *)&master->addr; - const struct sockaddr *src = (const struct sockaddr *)&master->via; + const struct sockaddr *dst = (const struct sockaddr *)&master.addr; + const struct sockaddr *src = (const struct sockaddr *)&master.via; struct knot_request *req = knot_request_make(re.mm, dst, src, query, 0); if (req == NULL) { knot_pkt_free(&query); @@ -366,7 +384,8 @@ static int forward_request(zone_t *zone, struct knot_request *request) /* Enqueue and execute request. */ ret = knot_requestor_enqueue(&re, req); if (ret == KNOT_EOK) { - struct timeval tv = { conf()->max_conn_reply, 0 }; + conf_val_t val = conf_get(conf(), C_SRV, C_MAX_CONN_REPLY); + struct timeval tv = { conf_int(&val), 0 }; ret = knot_requestor_exec(&re, &tv); } @@ -400,7 +419,7 @@ static void forward_requests(zone_t *zone, list_t *requests) static bool update_tsig_check(struct query_data *qdata, struct knot_request *req) { // Check that ACL is still valid. - if (!process_query_acl_check(&qdata->zone->conf->acl.update_in, qdata)) { + if (!process_query_acl_check(qdata->zone->name, ACL_ACTION_DDNS, qdata)) { UPDATE_LOG(LOG_WARNING, "ACL check failed"); knot_wire_set_rcode(req->resp->wire, qdata->rcode); return false; @@ -426,7 +445,7 @@ static bool update_tsig_check(struct query_data *qdata, struct knot_request *req static void send_update_response(const zone_t *zone, struct knot_request *req) { if (req->resp) { - if (!zone_master(zone)) { + if (zone_is_master(zone)) { // Sign the response with TSIG where applicable struct query_data qdata; init_qdata_from_request(&qdata, zone, req, NULL); @@ -475,7 +494,7 @@ static int init_update_responses(const zone_t *zone, list_t *updates, assert(req->query); knot_pkt_init_response(req->resp, req->query); - if (zone_master(zone)) { + if (!zone_is_master(zone)) { // Don't check TSIG for forwards. continue; } @@ -507,7 +526,7 @@ int update_query_process(knot_pkt_t *pkt, struct query_data *qdata) /* Need valid transaction security. */ zone_t *zone = (zone_t *)qdata->zone; - NS_NEED_AUTH(&zone->conf->acl.update_in, qdata); + NS_NEED_AUTH(qdata, zone->name, ACL_ACTION_DDNS); /* Check expiration. */ NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); @@ -533,22 +552,27 @@ int updates_execute(zone_t *zone) return KNOT_EOK; } + /* Block config changes. */ + rcu_read_lock(); + /* Init updates respones. */ int ret = init_update_responses(zone, &updates, &update_count); if (ret != KNOT_EOK) { /* Send what responses we can. */ set_rcodes(&updates, KNOT_RCODE_SERVFAIL); send_update_responses(zone, &updates); + rcu_read_unlock(); return ret; } if (update_count == 0) { /* All updates failed their ACL checks. */ + rcu_read_unlock(); return KNOT_EOK; } /* Process update list - forward if zone has master, or execute. */ - if (zone_master(zone)) { + if (!zone_is_master(zone)) { log_zone_info(zone->name, "DDNS, forwarding %zu updates", update_count); forward_requests(zone, &updates); @@ -562,5 +586,6 @@ int updates_execute(zone_t *zone) /* Send responses. */ send_update_responses(zone, &updates); + rcu_read_unlock(); return KNOT_EOK; } diff --git a/src/knot/server/journal.c b/src/knot/server/journal.c index cf7658e68b6f172b30e4faaba41abd7cebfe98fa..dc7167a07d2223caa39ce226c83aba501364129b 100644 --- a/src/knot/server/journal.c +++ b/src/knot/server/journal.c @@ -998,9 +998,10 @@ static int load_changeset(journal_t *journal, journal_node_t *n, const zone_t *z return KNOT_EOK; } -int journal_load_changesets(const zone_t *zone, list_t *dst, uint32_t from, uint32_t to) +int journal_load_changesets(const char *path, const zone_t *zone, list_t *dst, + uint32_t from, uint32_t to) { - int ret = journal_walk(zone->conf->ixfr_db, from, to, &load_changeset, zone, dst); + int ret = journal_walk(path, from, to, &load_changeset, zone, dst); if (ret != KNOT_EOK) { return ret; } diff --git a/src/knot/server/journal.h b/src/knot/server/journal.h index adf5cc1ebef44f5ed0e370e99d610aa298af4e90..1e502dc065abfac708fed48446cb1bbfb6c837d2 100644 --- a/src/knot/server/journal.h +++ b/src/knot/server/journal.h @@ -169,6 +169,7 @@ bool journal_exists(const char *path); * \brief Load changesets from journal. * * \param path Path to journal file. + * \param zone Corresponding zone. * \param dst Store changesets here. * \param from Start serial. * \param to End serial. @@ -177,7 +178,7 @@ bool journal_exists(const char *path); * \retval KNOT_ERANGE if given entry was not found. * \return < KNOT_EOK on error. */ -int journal_load_changesets(const struct zone *zone, list_t *dst, +int journal_load_changesets(const char *path, const struct zone *zone, list_t *dst, uint32_t from, uint32_t to); /*! diff --git a/src/knot/server/server.c b/src/knot/server/server.c index 30ba3af7d20a4338fdd2830122e1f7d8d7cd731e..9b76dbacd7232b76268ddbae83662b83b31065be 100644 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -14,6 +14,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <urcu.h> + #include <unistd.h> #include <stdio.h> #include <stdlib.h> @@ -93,23 +95,23 @@ static void server_remove_iface(iface_t *iface) * \retval 0 if successful (EOK). * \retval <0 on errors (EACCES, EINVAL, ENOMEM, EADDRINUSE). */ -static int server_init_iface(iface_t *new_if, conf_iface_t *cfg_if) +static int server_init_iface(iface_t *new_if, struct sockaddr_storage *addr) { /* Initialize interface. */ int ret = 0; memset(new_if, 0, sizeof(iface_t)); - memcpy(&new_if->addr, &cfg_if->addr, sizeof(struct sockaddr_storage)); + memcpy(&new_if->addr, addr, sizeof(struct sockaddr_storage)); /* Convert to string address format. */ - char addr_str[SOCKADDR_STRLEN] = {0}; - sockaddr_tostr(addr_str, sizeof(addr_str), &cfg_if->addr); + char addr_str[SOCKADDR_STRLEN] = { 0 }; + sockaddr_tostr(addr_str, sizeof(addr_str), addr); /* Create bound UDP socket. */ int bind_flags = 0; - int sock = net_bound_socket(SOCK_DGRAM, &cfg_if->addr, bind_flags); + int sock = net_bound_socket(SOCK_DGRAM, addr, bind_flags); if (sock == KNOT_EADDRNOTAVAIL) { bind_flags |= NET_BIND_NONLOCAL; - sock = net_bound_socket(SOCK_DGRAM, &cfg_if->addr, bind_flags); + sock = net_bound_socket(SOCK_DGRAM, addr, bind_flags); if (sock >= 0) { log_warning("address '%s' is not available", addr_str); } @@ -126,7 +128,7 @@ static int server_init_iface(iface_t *new_if, conf_iface_t *cfg_if) new_if->fd[IO_UDP] = sock; /* Create bound TCP socket. */ - sock = net_bound_socket(SOCK_STREAM, &cfg_if->addr, bind_flags); + sock = net_bound_socket(SOCK_STREAM, addr, bind_flags); if (sock < 0) { close(new_if->fd[IO_UDP]); return sock; @@ -180,12 +182,10 @@ static void remove_ifacelist(struct ref *p) * \param server Server instance. * \return number of added sockets. */ -static int reconfigure_sockets(const struct conf *conf, server_t *s) +static int reconfigure_sockets(conf_t *conf, server_t *s) { /* Prepare helper lists. */ - char addr_str[SOCKADDR_STRLEN] = {0}; int bound = 0; - iface_t *m = 0; ifacelist_t *oldlist = s->ifaces; ifacelist_t *newlist = malloc(sizeof(ifacelist_t)); ref_init(&newlist->ref, &remove_ifacelist); @@ -200,16 +200,19 @@ static int reconfigure_sockets(const struct conf *conf, server_t *s) } /* Update bound interfaces. */ - node_t *n = 0; - WALK_LIST(n, conf->ifaces) { + conf_val_t listen_val = conf_get(conf, C_SRV, C_LISTEN); + conf_val_t rundir_val = conf_get(conf, C_SRV, C_RUNDIR); + char *rundir = conf_abs_path(&rundir_val, NULL); + while (listen_val.code == KNOT_EOK) { + iface_t *m = NULL; /* Find already matching interface. */ int found_match = 0; - conf_iface_t *cfg_if = (conf_iface_t*)n; + struct sockaddr_storage addr = conf_addr(&listen_val, rundir); if (s->ifaces) { WALK_LIST(m, s->ifaces->u) { /* Matching port and address. */ - if (sockaddr_cmp(&cfg_if->addr, &m->addr) == 0) { + if (sockaddr_cmp(&addr, &m->addr) == 0) { found_match = 1; break; } @@ -220,12 +223,13 @@ static int reconfigure_sockets(const struct conf *conf, server_t *s) if (found_match) { rem_node((node_t *)m); } else { - sockaddr_tostr(addr_str, sizeof(addr_str), &cfg_if->addr); + char addr_str[SOCKADDR_STRLEN] = { 0 }; + sockaddr_tostr(addr_str, sizeof(addr_str), &addr); log_info("binding to interface '%s'", addr_str); /* Create new interface. */ m = malloc(sizeof(iface_t)); - if (server_init_iface(m, cfg_if) < 0) { + if (server_init_iface(m, &addr) < 0) { free(m); m = 0; } @@ -236,7 +240,10 @@ static int reconfigure_sockets(const struct conf *conf, server_t *s) add_tail(&newlist->l, (node_t *)m); ++bound; } + + conf_val_next(&listen_val); } + free(rundir); /* Wait for readers that are reconfiguring right now. */ /*! \note This subsystem will be reworked in #239 */ @@ -436,27 +443,52 @@ void server_wait(server_t *s) int server_reload(server_t *server, const char *cf) { - if (!server || !cf) { + if (server == NULL) { return KNOT_EINVAL; } - log_info("reloading configuration"); - int cf_ret = conf_open(cf); - switch (cf_ret) { - case KNOT_EOK: - log_info("configuration reloaded"); - break; - case KNOT_ENOENT: - log_error("configuration file '%s' not found", - conf()->filename); - break; - default: - log_error("failed to reload the configuration"); - break; + int ret = KNOT_EOK; + + if (cf != NULL) { + log_info("reloading configuration file '%s'", cf); + conf_t *new_conf = NULL; + ret = conf_clone(&new_conf); + if (ret != KNOT_EOK) { + log_fatal("failed to initialize configuration (%s)", + knot_strerror(ret)); + return EXIT_FAILURE; + } + + /* Import the configuration file. */ + ret = conf_import(new_conf, cf, true); + if (ret != KNOT_EOK) { + log_fatal("failed to load configuration file '%s' (%s)", + cf, knot_strerror(ret)); + conf_free(new_conf, false); + return EXIT_FAILURE; + } + + /* Run post-open config operations. */ + int res = conf_post_open(new_conf); + if (res != KNOT_EOK) { + log_fatal("failed to use configuration (%s)", + knot_strerror(res)); + conf_free(new_conf, false); + return EXIT_FAILURE; + } + + conf_update(new_conf); + } else { + log_info("reloading configuration database"); } - /*! \todo Close and bind to new remote control. */ - return cf_ret; + log_reconfigure(conf(), NULL); + server_reconfigure(conf(), server); + server_update_zones(conf(), server); + + log_info("configuration reloaded"); + + return ret; } void server_stop(server_t *server) @@ -476,7 +508,7 @@ void server_stop(server_t *server) } /*! \brief Reconfigure UDP and TCP query processing threads. */ -static int reconfigure_threads(const struct conf *conf, server_t *server) +static int reconfigure_threads(conf_t *conf, server_t *server) { /* Estimate number of threads/manager. */ int ret = KNOT_EOK; @@ -520,11 +552,15 @@ static int reconfigure_threads(const struct conf *conf, server_t *server) return ret; } -static int reconfigure_rate_limits(const struct conf *conf, server_t *server) +static int reconfigure_rate_limits(conf_t *conf, server_t *server) { + conf_val_t val = conf_get(conf, C_SRV, C_RATE_LIMIT); + int64_t rrl = conf_int(&val); + /* Rate limiting. */ - if (!server->rrl && conf->rrl > 0) { - server->rrl = rrl_create(conf->rrl_size); + if (!server->rrl && rrl > 0) { + val = conf_get(conf, C_SRV, C_RATE_LIMIT_SIZE); + server->rrl = rrl_create(conf_int(&val)); if (!server->rrl) { log_error("failed to initialize rate limiting table"); } else { @@ -532,16 +568,16 @@ static int reconfigure_rate_limits(const struct conf *conf, server_t *server) } } if (server->rrl) { - if (rrl_rate(server->rrl) != (uint32_t)conf->rrl) { + if (rrl_rate(server->rrl) != rrl) { /* We cannot free it, threads may use it. * Setting it to <1 will disable rate limiting. */ - if (conf->rrl < 1) { + if (rrl < 1) { log_info("rate limiting, disabled"); } else { - log_info("rate limiting, enabled with %u responses/second", - conf->rrl); + log_info("rate limiting, enabled with %i responses/second", + (int)rrl); } - rrl_setrate(server->rrl, conf->rrl); + rrl_setrate(server->rrl, rrl); } /* At this point, old buckets will converge to new rate. */ } @@ -549,7 +585,7 @@ static int reconfigure_rate_limits(const struct conf *conf, server_t *server) return KNOT_EOK; } -int server_reconfigure(const struct conf *conf, void *data) +int server_reconfigure(conf_t *conf, void *data) { server_t *server = (server_t *)data; dbg_server("%s(%p, %p)\n", __func__, conf, server); @@ -584,19 +620,22 @@ int server_reconfigure(const struct conf *conf, void *data) return ret; } -static void reopen_timers_database(const conf_t *conf, server_t *server) +static void reopen_timers_database(conf_t *conf, server_t *server) { close_timers_db(server->timers_db); server->timers_db = NULL; - int ret = open_timers_db(conf->storage, &server->timers_db); + conf_val_t val = conf_default_get(conf, C_STORAGE); + char *storage = conf_abs_path(&val, NULL); + int ret = open_timers_db(storage, &server->timers_db); + free(storage); if (ret != KNOT_EOK && ret != KNOT_ENOTSUP) { log_warning("cannot open persistent timers DB (%s)", knot_strerror(ret)); } } -int server_update_zones(const conf_t *conf, void *data) +int server_update_zones(conf_t *conf, void *data) { server_t *server = (server_t *)data; diff --git a/src/knot/server/server.h b/src/knot/server/server.h index 413e978cefd7e307a60e3f495ee67088500f111f..1ccff0dcc2ddea275d107e4e4a9fb7bcf7048043 100644 --- a/src/knot/server/server.h +++ b/src/knot/server/server.h @@ -178,7 +178,7 @@ void server_stop(server_t *server); * \retval KNOT_EINVAL on invalid parameters. * \retval KNOT_ERROR unspecified error. */ -int server_reconfigure(const struct conf *conf, void *data); +int server_reconfigure(conf_t *conf, void *data); /*! * \brief Reconfigure zone database. @@ -187,7 +187,7 @@ int server_reconfigure(const struct conf *conf, void *data); * * \return KNOT_EOK on success or KNOT_ error */ -int server_update_zones(const struct conf *conf, void *data); +int server_update_zones(conf_t *conf, void *data); /*! * \brief Update fdsets from current interfaces list. diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c index 3f848e71303c420e9c306e5570fa121e79781a6e..330745b408574d16f361b641aad3f87dd91d879b 100644 --- a/src/knot/server/tcp-handler.c +++ b/src/knot/server/tcp-handler.c @@ -24,6 +24,7 @@ #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> +#include <urcu.h> #ifdef HAVE_SYS_UIO_H // struct iovec (OpenBSD) #include <sys/uio.h> #endif // HAVE_SYS_UIO_H @@ -112,20 +113,20 @@ static int tcp_handle(tcp_context_t *tcp, int fd, /* Timeout. */ rcu_read_lock(); - struct timeval tmout = { conf()->max_conn_reply, 0 }; + conf_val_t val = conf_get(conf(), C_SRV, C_MAX_CONN_REPLY); + int64_t max_conn_reply = conf_int(&val); rcu_read_unlock(); + struct timeval tmout = { max_conn_reply, 0 }; /* Receive data. */ int ret = tcp_recv_msg(fd, rx->iov_base, rx->iov_len, &tmout); if (ret <= 0) { dbg_net("tcp: client on fd=%d disconnected\n", fd); if (ret == KNOT_EAGAIN) { - rcu_read_lock(); char addr_str[SOCKADDR_STRLEN] = {0}; sockaddr_tostr(addr_str, sizeof(addr_str), &ss); log_warning("TCP, connection timed out, address '%s'", addr_str); - rcu_read_unlock(); } return KNOT_ECONNREFUSED; } else { @@ -187,7 +188,8 @@ int tcp_accept(int fd) #ifdef SO_RCVTIMEO struct timeval tv; rcu_read_lock(); - tv.tv_sec = conf()->max_conn_idle; + conf_val_t val = conf_get(conf(), C_SRV, C_MAX_CONN_IDLE); + tv.tv_sec = conf_int(&val); rcu_read_unlock(); tv.tv_usec = 0; if (setsockopt(incoming, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { @@ -215,7 +217,8 @@ static int tcp_event_accept(tcp_context_t *tcp, unsigned i) /* Update watchdog timer. */ rcu_read_lock(); - fdset_set_watchdog(&tcp->set, next_id, conf()->max_conn_hs); + conf_val_t val = conf_get(conf(), C_SRV, C_MAX_CONN_HANDSHAKE); + fdset_set_watchdog(&tcp->set, next_id, conf_int(&val)); rcu_read_unlock(); return KNOT_EOK; @@ -235,7 +238,8 @@ static int tcp_event_serve(tcp_context_t *tcp, unsigned i) if (ret == KNOT_EOK) { /* Update socket activity timer. */ rcu_read_lock(); - fdset_set_watchdog(&tcp->set, i, conf()->max_conn_idle); + conf_val_t val = conf_get(conf(), C_SRV, C_MAX_CONN_IDLE); + fdset_set_watchdog(&tcp->set, i, conf_int(&val)); rcu_read_unlock(); } @@ -254,7 +258,8 @@ static int tcp_wait_for_events(tcp_context_t *tcp) if (!is_throttled) { /* Configuration limit, infer maximal pool size. */ rcu_read_lock(); - unsigned max_per_set = MAX(conf()->max_tcp_clients / conf_tcp_threads(conf()), 1); + conf_val_t val = conf_get(conf(), C_SRV, C_MAX_TCP_CLIENTS); + unsigned max_per_set = MAX(conf_int(&val) / conf_tcp_threads(conf()), 1); rcu_read_unlock(); /* Subtract master sockets check limits. */ is_throttled = (set->n - tcp->client_threshold) >= max_per_set; @@ -320,7 +325,8 @@ int tcp_master(dthread_t *thread) tcp.overlay.mm = &mm; /* Prepare structures for bound sockets. */ - fdset_init(&tcp.set, list_size(&conf()->ifaces) + CONFIG_XFERS); + conf_val_t val = conf_get(conf(), C_SRV, C_LISTEN); + fdset_init(&tcp.set, conf_val_count(&val) + CONF_XFERS); /* Create iovec abstraction. */ for (unsigned i = 0; i < 2; ++i) { diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c index 4bc6e484c72ae8505331903f0ac25b602f6cbae4..77fcc64a687ced6f1ee92aed1f272020833c8ffa 100644 --- a/src/knot/server/udp-handler.c +++ b/src/knot/server/udp-handler.c @@ -29,6 +29,7 @@ #include <errno.h> #include <limits.h> #include <sys/param.h> +#include <urcu.h> #ifdef HAVE_SYS_UIO_H /* 'struct iovec' for OpenBSD */ #include <sys/uio.h> #endif /* HAVE_SYS_UIO_H */ diff --git a/src/knot/updates/acl.c b/src/knot/updates/acl.c index e8fb837bd3d0a8f85cc3a4f33093f8c6afd58b4e..3894835acc3a0999bf545ec624903545e3a6fd53 100644 --- a/src/knot/updates/acl.c +++ b/src/knot/updates/acl.c @@ -22,135 +22,155 @@ #include <limits.h> #include <stdbool.h> -#include "libknot/libknot.h" #include "knot/updates/acl.h" #include "knot/conf/conf.h" +#include "libknot/libknot.h" #include "libknot/internal/endian.h" -#include "libknot/rrtype/tsig.h" +#include "libknot/internal/sockaddr.h" -static inline uint32_t ipv4_chunk(const struct sockaddr_in *ipv4) -{ - /* Stored as big end first. */ - return ipv4->sin_addr.s_addr; +static const uint8_t* ipv4_addr(const struct sockaddr_storage *ss) { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)ss; + return (uint8_t *)&ipv4->sin_addr.s_addr; } -static inline uint32_t ipv6_chunk(const struct sockaddr_in6 *ipv6, uint8_t idx) -{ - /* Is byte array, 4x 32bit value. */ - return ((uint32_t *)&ipv6->sin6_addr)[idx]; +static const uint8_t* ipv6_addr(const struct sockaddr_storage *ss) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)ss; + return (uint8_t *)&ipv6->sin6_addr.s6_addr; } -static inline uint32_t ip_chunk(const struct sockaddr_storage *ss, uint8_t idx) +bool netblock_match(const struct sockaddr_storage *ss1, + const struct sockaddr_storage *ss2, + unsigned prefix) { - if (ss->ss_family == AF_INET6) { - return ipv6_chunk((const struct sockaddr_in6 *)ss, idx); - } else { - return ipv4_chunk((const struct sockaddr_in *)ss); + if (ss1 == NULL || ss2 == NULL) { + return false; } -} - -/*! \brief Compare chunks using given mask. */ -static int cmp_chunk(const conf_iface_t *a1, const struct sockaddr_storage *a2, - uint8_t idx, uint32_t mask) -{ - const uint32_t c1 = ip_chunk(&a1->addr, idx) & mask; - const uint32_t c2 = ip_chunk(a2, idx) & mask; - - if (c1 > c2) - return 1; - if (c1 < c2) - return -1; - return 0; -} -/*! - * \brief Calculate bitmask for byte array from the MSB. - * - * \note i.e. 8 means top 8 bits set, 11111111000000000000000000000000 - * - * \param nbits number of bits set to 1 - * \return mask - */ -static uint32_t acl_fill_mask32(short nbits) -{ - assert(nbits >= 0 && nbits <= 32); - uint32_t r = 0; - for (char i = 0; i < nbits; ++i) { - r |= 1 << (31 - i); + if (ss1->ss_family != ss2->ss_family) { + return false; } - /* Make sure the mask is in network byte order. */ - return htonl(r); -} - -int netblock_match(struct conf_iface *a1, const struct sockaddr_storage *a2) -{ - int ret = 0; - uint32_t mask = 0xffffffff; - short mask_bits = a1->prefix; - const short chunk_bits = sizeof(mask) * CHAR_BIT; - - /* Check different length, IPv4 goes first. */ - if (a1->addr.ss_family != a2->ss_family) { - if (a1->addr.ss_family < a2->ss_family) { - return -1; - } else { - return 1; - } + const uint8_t *addr1, *addr2; + switch (ss1->ss_family) { + case AF_INET: + addr1 = ipv4_addr(ss1); + addr2 = ipv4_addr(ss2); + prefix = prefix > IPV4_PREFIXLEN ? IPV4_PREFIXLEN : prefix; + break; + case AF_INET6: + addr1 = ipv6_addr(ss1); + addr2 = ipv6_addr(ss2); + prefix = prefix > IPV6_PREFIXLEN ? IPV6_PREFIXLEN : prefix; + break; + default: + return false; } - /* At most 4xchunk_bits for IPv6 */ - unsigned i = 0; - while (ret == 0 && mask_bits > 0) { - /* Compute mask for current chunk. */ - if (mask_bits <= chunk_bits) { - mask = acl_fill_mask32(mask_bits); - mask_bits = 0; /* Last chunk */ - } else { - mask_bits -= chunk_bits; + /* Compare full bytes address block. */ + uint8_t full_bytes = prefix / 8; + for (int i = 0; i < full_bytes; i++) { + if (addr1[i] != addr2[i]) { + return false; } + } - /* Empty mask - shortcut, we're done. */ - if (mask > 0) { - ret = cmp_chunk(a1, a2, i, mask); + /* Compare last partial byte address block. */ + uint8_t rest_bits = prefix % 8; + if (rest_bits > 0) { + uint8_t rest1 = addr1[full_bytes] >> (8 - rest_bits); + uint8_t rest2 = addr2[full_bytes] >> (8 - rest_bits); + if (rest1 != rest2) { + return false; } - ++i; } - return ret; + return true; } -struct conf_iface* acl_find(list_t *acl, const struct sockaddr_storage *addr, - const knot_dname_t *key_name) +bool acl_allowed(conf_val_t *acl, acl_action_t action, + const struct sockaddr_storage *addr, + knot_tsig_key_t *tsig) { - if (acl == NULL || addr == NULL) { + if (acl == NULL || addr == NULL || tsig == NULL) { return NULL; } - conf_remote_t *remote = NULL; - WALK_LIST(remote, *acl) { - conf_iface_t *cur = remote->remote; - if (netblock_match(cur, addr) == 0) { - /* NOKEY entry. */ - if (cur->key == NULL) { - if (key_name == NULL) { - return cur; - } - /* NOKEY entry, but key provided. */ + while (acl->code == KNOT_EOK) { + /* Check if the action is allowed. */ + bool match = false, deny = false; + conf_val_t action_val = conf_id_get(conf(), C_ACL, C_ACTION, acl); + while (action_val.code == KNOT_EOK) { + unsigned act = conf_opt(&action_val); + if (act & action) { + match = true; + } + if (act == ACL_ACTION_DENY) { + deny = true; + } + conf_val_next(&action_val); + } + if (!match) { + conf_val_next(acl); + continue; + } + + /* Check if the address prefix matches. */ + conf_val_t addr_val = conf_id_get(conf(), C_ACL, C_ADDR, acl); + if (addr_val.code == KNOT_EOK) { + unsigned prefix; + struct sockaddr_storage ss; + ss = conf_net(&addr_val, &prefix); + if (!netblock_match(addr, &ss, prefix)) { + conf_val_next(acl); continue; } + } - /* NOKEY provided, but key required. */ - if (key_name == NULL) { + /* Check if the key matches. */ + conf_val_t key_val = conf_id_get(conf(), C_ACL, C_KEY, acl); + if (key_val.code == KNOT_EOK) { + /* No key provided, but required. */ + if (tsig->name == NULL) { + conf_val_next(acl); continue; } - /* Key name match. */ - if (knot_dname_is_equal(cur->key->name, key_name)) { - return cur; + /* Compare key names. */ + const knot_dname_t *key_name = conf_dname(&key_val); + if (knot_dname_cmp(key_name, tsig->name) != 0) { + conf_val_next(acl); + continue; } + + /* Compare key algorithms. */ + conf_val_t alg_val = conf_id_get(conf(), C_KEY, C_ALG, + &key_val); + if (conf_opt(&alg_val) != tsig->algorithm) { + conf_val_next(acl); + continue; + } + /* No key required, but provided. */ + } else if (tsig->name != NULL) { + conf_val_next(acl); + continue; + } + + if (deny) { + conf_val_next(acl); + continue; } + + /* Fill the output with tsig secret. */ + if (tsig->name != NULL) { + conf_val_t secret_val = conf_id_get(conf(), C_KEY, + C_SECRET, &key_val); + conf_data(&secret_val); + tsig->secret.data = (uint8_t *)secret_val.data; + tsig->secret.size = secret_val.len; + } + + return true; } - return NULL; + return false; } diff --git a/src/knot/updates/acl.h b/src/knot/updates/acl.h index 30885fce12b5b321e1c58d4488efa4f2fed72315..f271729d2733629c9d9ae804753c8cb171a2562e 100644 --- a/src/knot/updates/acl.h +++ b/src/knot/updates/acl.h @@ -14,16 +14,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ /*! - * \file acl.h + * \file * - * \author Marek Vavrusa <marek.vavrusa@nic.cz> + * Access control list. * - * \brief Access control lists. - * - * Simple access control list is implemented as a linked list, sorted by - * prefix length. This way, longest prefix match is always found first. - * - * \addtogroup common_lib + * \addtogroup server * @{ */ @@ -33,23 +28,44 @@ #include "libknot/internal/sockaddr.h" #include "libknot/internal/mempattern.h" #include "libknot/rrtype/tsig.h" +#include "knot/conf/conf.h" -struct conf_iface; +/*! \brief ACL actions. */ +typedef enum { + ACL_ACTION_DENY = 0, + ACL_ACTION_XFER = 1, + ACL_ACTION_NOTF = 2, + ACL_ACTION_DDNS = 3, + ACL_ACTION_CNTL = 4 +} acl_action_t; -/*! \brief Match address against netblock. */ -int netblock_match(struct conf_iface *a1, const struct sockaddr_storage *a2); +/*! + * \brief Checks if two netblocks match. + * + * \param ss1 First address storage. + * \param ss2 Second address storage. + * \param prefix Netblock length. + */ +bool netblock_match(const struct sockaddr_storage *ss1, + const struct sockaddr_storage *ss2, + unsigned prefix); /*! - * \brief Match address against ACL. + * \brief Checks if the address and/or tsig key matches given ACL list. + * + * If a proper ACL rule is found and tsig.name is not empty, + * tsig.secret is filled. * - * \param acl Pointer to ACL instance. - * \param addr IP address. - * \param key_name TSIG key name (optional) + * \param acl Pointer to ACL config multivalued identifier. + * \param action ACL action. + * \param addr IP address. + * \param tsig TSIG parameters. * - * \retval Matching rule instance if found. - * \retval NULL if it didn't find a match. + * \retval true if authenticated. + * \retval false if not authenticated. */ -struct conf_iface* acl_find(list_t *acl, const struct sockaddr_storage *addr, - const knot_dname_t *key_name); +bool acl_allowed(conf_val_t *acl, acl_action_t action, + const struct sockaddr_storage *addr, + knot_tsig_key_t *tsig); /*! @} */ diff --git a/src/knot/updates/apply.c b/src/knot/updates/apply.c index a2f1a3d020804c37c8789f51f9e4379136d34b96..d372403c47f8235ba86080ab8aa15cab6ffb9414 100644 --- a/src/knot/updates/apply.c +++ b/src/knot/updates/apply.c @@ -15,6 +15,7 @@ */ #include <assert.h> +#include <urcu.h> #include "knot/updates/apply.h" @@ -453,10 +454,13 @@ int apply_changesets(zone_t *zone, list_t *chsets, zone_contents_t **new_content /* * Apply the changesets. */ + rcu_read_lock(); + bool is_master = zone_is_master(zone); + rcu_read_unlock(); + changeset_t *set = NULL; - const bool master = (zone_master(zone) == NULL); WALK_LIST(set, *chsets) { - ret = apply_single(contents_copy, set, master); + ret = apply_single(contents_copy, set, is_master); if (ret != KNOT_EOK) { updates_rollback(chsets); update_free_zone(&contents_copy); @@ -495,8 +499,11 @@ int apply_changeset(zone_t *zone, changeset_t *change, zone_contents_t **new_con return ret; } - const bool master = (zone_master(zone) == NULL); - ret = apply_single(contents_copy, change, master); + rcu_read_lock(); + bool is_master = zone_is_master(zone); + rcu_read_unlock(); + + ret = apply_single(contents_copy, change, is_master); if (ret != KNOT_EOK) { update_rollback(change); update_free_zone(&contents_copy); diff --git a/src/knot/updates/ddns.c b/src/knot/updates/ddns.c index 418bebbe349ccf8f766488c0c5f28d879377d5c0..ed90723dbbba4a469a849c6a38b1deb58e80aa9c 100644 --- a/src/knot/updates/ddns.c +++ b/src/knot/updates/ddns.c @@ -17,7 +17,9 @@ #include <assert.h> #include <stdlib.h> #include <inttypes.h> +#include <urcu.h> +#include "knot/common/log.h" #include "knot/updates/ddns.h" #include "knot/updates/changesets.h" #include "knot/updates/zone-update.h" @@ -971,8 +973,9 @@ int ddns_process_update(const zone_t *zone, const knot_pkt_t *query, return KNOT_ENOMEM; } + conf_val_t val = conf_zone_get(conf(), C_SERIAL_POLICY, zone->name); uint32_t old_serial = knot_soa_serial(&soa_cpy->rrs); - uint32_t new_serial = serial_next(old_serial, zone->conf->serial_policy); + uint32_t new_serial = serial_next(old_serial, conf_opt(&val)); if (serial_compare(old_serial, new_serial) >= 0) { log_zone_warning(zone->name, "updated serial is lower " "than current, serial %u -> %u", diff --git a/src/knot/zone/events/events.c b/src/knot/zone/events/events.c index d1d03e523caae0cd1446f444bfc9c853f3d62ffe..37ec9dd109b8d2d6c7bde35d859d58439d320833 100644 --- a/src/knot/zone/events/events.c +++ b/src/knot/zone/events/events.c @@ -18,6 +18,7 @@ #include <time.h> #include "knot/common/evsched.h" +#include "knot/common/log.h" #include "libknot/libknot.h" #include "libknot/internal/namedb/namedb.h" #include "knot/server/server.h" diff --git a/src/knot/zone/events/handlers.c b/src/knot/zone/events/handlers.c index 1a43c96737d2713c4c8bab0d9749f45245c28d7e..f1278d024480d99c81b8e984467509b44b30fd3b 100644 --- a/src/knot/zone/events/handlers.c +++ b/src/knot/zone/events/handlers.c @@ -14,15 +14,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <urcu.h> + #include "dnssec/random.h" #include "libknot/libknot.h" -#include "libknot/processing/requestor.h" -#include "libknot/rrtype/soa.h" - -#include "knot/common/trim.h" #include "libknot/internal/mempool.h" #include "libknot/internal/macros.h" +#include "libknot/processing/requestor.h" +#include "knot/common/log.h" +#include "knot/common/trim.h" #include "knot/server/udp-handler.h" #include "knot/server/tcp-handler.h" #include "knot/updates/changesets.h" @@ -46,7 +47,7 @@ /*! \brief Zone event logging. */ #define ZONE_QUERY_LOG(severity, zone, remote, operation, msg...) \ - NS_PROC_LOG(severity, &remote->addr, zone->name, operation, msg) + NS_PROC_LOG(severity, &(remote)->addr, zone->name, operation, msg) /*! \brief Create zone query packet. */ static knot_pkt_t *zone_query(const zone_t *zone, uint16_t pkt_type, mm_ctx_t *mm) @@ -91,7 +92,7 @@ static knot_pkt_t *zone_query(const zone_t *zone, uint16_t pkt_type, mm_ctx_t *m * \note Everything in this function is executed synchronously, returns when * the query processing is either complete or an error occurs. */ -static int zone_query_execute(zone_t *zone, uint16_t pkt_type, const conf_iface_t *remote) +static int zone_query_execute(zone_t *zone, uint16_t pkt_type, const conf_remote_t *remote) { /* Create a memory pool for this task. */ int ret = KNOT_EOK; @@ -115,7 +116,9 @@ static int zone_query_execute(zone_t *zone, uint16_t pkt_type, const conf_iface_ knot_requestor_init(&re, &mm); knot_requestor_overlay(&re, KNOT_STATE_ANSWER, ¶m); - tsig_init(¶m.tsig_ctx, remote->key); + const knot_tsig_key_t *key = remote->key.name != NULL ? + &remote->key : NULL; + tsig_init(¶m.tsig_ctx, key); ret = tsig_sign_packet(¶m.tsig_ctx, query); if (ret != KNOT_EOK) { @@ -134,7 +137,8 @@ static int zone_query_execute(zone_t *zone, uint16_t pkt_type, const conf_iface_ /* Send the queries and process responses. */ ret = knot_requestor_enqueue(&re, req); if (ret == KNOT_EOK) { - struct timeval tv = { conf()->max_conn_reply, 0 }; + conf_val_t val = conf_get(conf(), C_SRV, C_MAX_CONN_REPLY); + struct timeval tv = { conf_int(&val), 0 }; ret = knot_requestor_exec(&re, &tv); } @@ -156,7 +160,7 @@ fail: } /*! \brief Execute zone transfer request. */ -static int zone_query_transfer(zone_t *zone, const conf_iface_t *master, uint16_t pkt_type) +static int zone_query_transfer(zone_t *zone, const conf_remote_t *master, uint16_t pkt_type) { assert(zone); assert(master); @@ -215,7 +219,8 @@ static uint32_t soa_graceful_expire(const knot_rdataset_t *soa) { // Allow for timeouts. Otherwise zones with very short // expiry may expire before the timeout is reached. - return knot_soa_expire(soa) + 2 * conf()->max_conn_idle; + conf_val_t val = conf_get(conf(), C_SRV, C_MAX_CONN_IDLE); + return knot_soa_expire(soa) + 2 * conf_int(&val); } /*! \brief Schedule expire event, unless it is already scheduled. */ @@ -235,24 +240,25 @@ int event_reload(zone_t *zone) assert(zone); /* Take zone file mtime and load it. */ - time_t mtime = zonefile_mtime(zone->conf->file); + char *filename = conf_zonefile(conf(), zone->name); + time_t mtime = zonefile_mtime(filename); + free(filename); uint32_t dnssec_refresh = time(NULL); - conf_zone_t *zone_config = zone->conf; - zone_contents_t *contents = zone_load_contents(zone_config); + zone_contents_t *contents = zone_load_contents(conf(), zone->name); if (!contents) { return KNOT_ERROR; } /* Store zonefile serial and apply changes from the journal. */ zone->zonefile_serial = zone_contents_serial(contents); - int result = zone_load_journal(zone, contents); + int result = zone_load_journal(conf(), zone, contents); if (result != KNOT_EOK) { goto fail; } /* Post load actions - calculate delta, sign with DNSSEC... */ /*! \todo issue #242 dnssec signing should occur in the special event */ - result = zone_load_post(contents, zone, &dnssec_refresh); + result = zone_load_post(conf(), contents, zone, &dnssec_refresh); if (result != KNOT_EOK) { if (result == KNOT_ESPACE) { log_zone_error(zone->name, "journal size is too small " @@ -265,7 +271,7 @@ int event_reload(zone_t *zone) } /* Check zone contents consistency. */ - result = zone_load_check(contents, zone_config); + result = zone_load_check(conf(), contents); if (result != KNOT_EOK) { goto fail; } @@ -280,7 +286,7 @@ int event_reload(zone_t *zone) } /* Schedule notify and refresh after load. */ - if (zone_master(zone)) { + if (!zone_is_master(zone)) { zone_events_schedule(zone, ZONE_EVENT_REFRESH, ZONE_EVENT_NOW); } if (!zone_contents_is_empty(contents)) { @@ -289,12 +295,14 @@ int event_reload(zone_t *zone) } /* Schedule zone resign. */ - if (zone->conf->dnssec_enable) { + conf_val_t val = conf_zone_get(conf(), C_DNSSEC_ENABLE, zone->name); + if (conf_bool(&val)) { schedule_dnssec(zone, dnssec_refresh); } /* Periodic execution. */ - zone_events_schedule(zone, ZONE_EVENT_FLUSH, zone_config->dbsync_timeout); + val = conf_zone_get(conf(), C_ZONEFILE_SYNC, zone->name); + zone_events_schedule(zone, ZONE_EVENT_FLUSH, conf_int(&val)); uint32_t current_serial = zone_contents_serial(zone->contents); log_zone_info(zone->name, "loaded, serial %u -> %u", @@ -311,9 +319,16 @@ int event_refresh(zone_t *zone) { assert(zone); - const conf_iface_t *master = zone_master(zone); - if (master == NULL) { - /* If not slave zone, ignore. */ + /* Ignore if master zone. */ + if (zone_is_master(zone)) { + return KNOT_EOK; + } + + /* Ignore if DNSSEC enabled. */ + conf_val_t val = conf_zone_get(conf(), C_DNSSEC_ENABLE, zone->name); + if (conf_bool(&val)) { + log_zone_notice(zone->name, "ignoring zone refresh due to " + "enabled automatic DNSSEC signing."); return KNOT_EOK; } @@ -323,11 +338,12 @@ int event_refresh(zone_t *zone) return KNOT_EOK; } - int ret = zone_query_execute(zone, KNOT_QUERY_NORMAL, master); + const conf_remote_t master = zone_master(zone); + int ret = zone_query_execute(zone, KNOT_QUERY_NORMAL, &master); const knot_rdataset_t *soa = zone_soa(zone); if (ret != KNOT_EOK) { /* Log connection errors. */ - ZONE_QUERY_LOG(LOG_WARNING, zone, master, "SOA query, outgoing", + ZONE_QUERY_LOG(LOG_WARNING, zone, &master, "SOA query, outgoing", "failed (%s)", knot_strerror(ret)); /* Rotate masters if current failed. */ zone_master_rotate(zone); @@ -346,9 +362,16 @@ int event_xfer(zone_t *zone) { assert(zone); - const conf_iface_t *master = zone_master(zone); - if (master == NULL) { - /* If not slave zone, ignore. */ + /* Ignore if master zone. */ + if (zone_is_master(zone)) { + return KNOT_EOK; + } + + /* Ignore if DNSSEC enabled. */ + conf_val_t val = conf_zone_get(conf(), C_DNSSEC_ENABLE, zone->name); + if (conf_bool(&val)) { + log_zone_notice(zone->name, "ignoring slave transfer due to " + "enabled automatic DNSSEC signing."); return KNOT_EOK; } @@ -360,7 +383,8 @@ int event_xfer(zone_t *zone) } /* Execute zone transfer and reschedule timers. */ - int ret = zone_query_transfer(zone, master, pkt_type); + const conf_remote_t master = zone_master(zone); + int ret = zone_query_transfer(zone, &master, pkt_type); /* Handle failure during transfer. */ if (ret != KNOT_EOK) { @@ -383,10 +407,12 @@ int event_xfer(zone_t *zone) zone_events_schedule(zone, ZONE_EVENT_REFRESH, knot_soa_refresh(soa)); zone_events_schedule(zone, ZONE_EVENT_NOTIFY, ZONE_EVENT_NOW); zone_events_cancel(zone, ZONE_EVENT_EXPIRE); - if (zone->conf->dbsync_timeout == 0) { + val = conf_zone_get(conf(), C_ZONEFILE_SYNC, zone->name); + int64_t dbsync_timeout = conf_int(&val); + if (dbsync_timeout == 0) { zone_events_schedule(zone, ZONE_EVENT_FLUSH, ZONE_EVENT_NOW); } else if (!zone_events_is_scheduled(zone, ZONE_EVENT_FLUSH)) { - zone_events_schedule(zone, ZONE_EVENT_FLUSH, zone->conf->dbsync_timeout); + zone_events_schedule(zone, ZONE_EVENT_FLUSH, dbsync_timeout); } /* Transfer cleanup. */ @@ -451,7 +477,8 @@ int event_flush(zone_t *zone) assert(zone); /* Reschedule. */ - int next_timeout = zone->conf->dbsync_timeout; + conf_val_t val = conf_zone_get(conf(), C_ZONEFILE_SYNC, zone->name); + int64_t next_timeout = conf_int(&val); if (next_timeout > 0) { zone_events_schedule(zone, ZONE_EVENT_FLUSH, next_timeout); } @@ -474,19 +501,22 @@ int event_notify(zone_t *zone) } /* Walk through configured remotes and send messages. */ - conf_remote_t *remote = 0; - WALK_LIST(remote, zone->conf->acl.notify_out) { - conf_iface_t *iface = remote->remote; + conf_val_t val = conf_zone_get(conf(), C_NOTIFY, zone->name); + while (val.code == KNOT_EOK) { + conf_remote_t remote = conf_remote(conf(), &val); - int ret = zone_query_execute(zone, KNOT_QUERY_NOTIFY, iface); + int ret = zone_query_execute(zone, KNOT_QUERY_NOTIFY, &remote); if (ret == KNOT_EOK) { - ZONE_QUERY_LOG(LOG_INFO, zone, iface, "NOTIFY, outgoing", - "serial %u", + ZONE_QUERY_LOG(LOG_INFO, zone, &remote, + "NOTIFY, outgoing", "serial %u", zone_contents_serial(zone->contents)); } else { - ZONE_QUERY_LOG(LOG_WARNING, zone, iface, "NOTIFY, outgoing", - "failed (%s)", knot_strerror(ret)); + ZONE_QUERY_LOG(LOG_WARNING, zone, &remote, + "NOTIFY, outgoing", "failed (%s)", + knot_strerror(ret)); } + + conf_val_next(&val); } return KNOT_EOK; @@ -515,7 +545,7 @@ int event_dnssec(zone_t *zone) sign_flags = 0; } - ret = knot_dnssec_zone_sign(zone->contents, zone->conf, &ch, sign_flags, &refresh_at); + ret = knot_dnssec_zone_sign(zone->contents, &ch, sign_flags, &refresh_at); if (ret != KNOT_EOK) { goto done; } @@ -552,7 +582,8 @@ int event_dnssec(zone_t *zone) schedule_dnssec(zone, refresh_at); zone_events_schedule(zone, ZONE_EVENT_NOTIFY, ZONE_EVENT_NOW); - if (zone->conf->dbsync_timeout == 0) { + conf_val_t val = conf_zone_get(conf(), C_ZONEFILE_SYNC, zone->name); + if (conf_int(&val) == 0) { zone_events_schedule(zone, ZONE_EVENT_FLUSH, ZONE_EVENT_NOW); } @@ -573,3 +604,4 @@ uint32_t bootstrap_next(uint32_t timer) } return timer; } + diff --git a/src/knot/zone/events/replan.c b/src/knot/zone/events/replan.c index 6823badd65b004215d54acea26010374ac7a0380..7147e64a6dc799a17afae5bc0c7728a080dd72a2 100644 --- a/src/knot/zone/events/replan.c +++ b/src/knot/zone/events/replan.c @@ -35,12 +35,12 @@ static void replan_event(zone_t *zone, const zone_t *old_zone, zone_event_type_t /*!< \brief Replans events that are dependent on the SOA record. */ static void replan_soa_events(zone_t *zone, const zone_t *old_zone) { - if (!zone_master(zone)) { + if (zone_is_master(zone)) { // Events only valid for slaves. return; } - if (zone_master(old_zone)) { + if (!zone_is_master(old_zone)) { // Replan SOA events. replan_event(zone, old_zone, ZONE_EVENT_REFRESH); replan_event(zone, old_zone, ZONE_EVENT_EXPIRE); @@ -58,12 +58,12 @@ static void replan_soa_events(zone_t *zone, const zone_t *old_zone) /*!< \brief Replans transfer event. */ static void replan_xfer(zone_t *zone, const zone_t *old_zone) { - if (!zone_master(zone)) { + if (zone_is_master(zone)) { // Only valid for slaves. return; } - if (zone_master(old_zone)) { + if (!zone_is_master(old_zone)) { // Replan the transfer from old zone. replan_event(zone, old_zone, ZONE_EVENT_XFER); } else if (zone_contents_is_empty(zone->contents)) { @@ -76,7 +76,9 @@ static void replan_xfer(zone_t *zone, const zone_t *old_zone) /*!< \brief Replans flush event. */ static void replan_flush(zone_t *zone, const zone_t *old_zone) { - if (zone->conf->dbsync_timeout <= 0) { + conf_val_t val = conf_zone_get(conf(), C_ZONEFILE_SYNC, zone->name); + int64_t dbsync_timeout = conf_int(&val); + if (dbsync_timeout <= 0) { // Immediate sync scheduled after events. return; } @@ -84,12 +86,12 @@ static void replan_flush(zone_t *zone, const zone_t *old_zone) const time_t flush_time = zone_events_get_time(old_zone, ZONE_EVENT_FLUSH); if (flush_time <= ZONE_EVENT_NOW) { // Not scheduled previously. - zone_events_schedule(zone, ZONE_EVENT_FLUSH, zone->conf->dbsync_timeout); + zone_events_schedule(zone, ZONE_EVENT_FLUSH, dbsync_timeout); return; } // Pick time to schedule: either reuse or schedule sooner than old event. - const time_t schedule_at = MIN(time(NULL) + zone->conf->dbsync_timeout, flush_time); + const time_t schedule_at = MIN(time(NULL) + dbsync_timeout, flush_time); zone_events_schedule_at(zone, ZONE_EVENT_FLUSH, schedule_at); } @@ -109,7 +111,8 @@ static void duplicate_ddns_q(zone_t *zone, zone_t *old_zone) /*!< Replans DNSSEC event. Not whole resign needed, \todo #247 */ static void replan_dnssec(zone_t *zone) { - if (zone->conf->dnssec_enable) { + conf_val_t val = conf_zone_get(conf(), C_DNSSEC_ENABLE, zone->name); + if (conf_bool(&val)) { /* Keys could have changed, force resign. */ zone_events_schedule(zone, ZONE_EVENT_DNSSEC, ZONE_EVENT_NOW); } diff --git a/src/knot/zone/serial.c b/src/knot/zone/serial.c index e622b307b2e09a028da8a19ab257c7452173bb7d..271d7016a6993514f6f0bfc3fa3b99a8c4a0b80f 100644 --- a/src/knot/zone/serial.c +++ b/src/knot/zone/serial.c @@ -38,9 +38,9 @@ int serial_compare(uint32_t s1, uint32_t s2) int serial_next(uint32_t current, int policy) { switch (policy) { - case CONF_SERIAL_INCREMENT: + case SERIAL_POLICY_INCREMENT: return current + 1; - case CONF_SERIAL_UNIXTIME: + case SERIAL_POLICY_UNIXTIME: return time(NULL); default: assert(0); diff --git a/src/knot/zone/serial.h b/src/knot/zone/serial.h index 2fd3a4f7ac7ea196dbe21aa0e22bfc191bee77ef..3db76bb9bd2d996909ad315a543f766095e3454d 100644 --- a/src/knot/zone/serial.h +++ b/src/knot/zone/serial.h @@ -31,7 +31,7 @@ int serial_compare(uint32_t s1, uint32_t s2); * \brief Get next serial for given serial update policy. * * \param current Current SOA serial. - * \param policy CONF_SERIAL_INCREMENT or CONF_SERIAL_UNIXTIME. + * \param policy SERIAL_POLICY_INCREMENT or SERIAL_POLICY_UNIXTIME. * * \return New serial. */ diff --git a/src/knot/zone/zone-load.c b/src/knot/zone/zone-load.c index d52ab2ae27d637413e758c49ae52d932fc612dfb..b087f59926b27db768df3762ee81aa18ee1b985a 100644 --- a/src/knot/zone/zone-load.c +++ b/src/knot/zone/zone-load.c @@ -14,7 +14,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "libknot/libknot.h" #include "knot/common/log.h" #include "knot/server/journal.h" #include "knot/zone/zone-diff.h" @@ -23,15 +22,18 @@ #include "knot/zone/zonefile.h" #include "knot/dnssec/zone-events.h" #include "knot/updates/apply.h" -#include "libknot/rdata.h" +#include "libknot/libknot.h" -zone_contents_t *zone_load_contents(conf_zone_t *zone_config) +zone_contents_t *zone_load_contents(conf_t *conf, const knot_dname_t *zone_name) { - assert(zone_config); + assert(conf); + assert(zone_name); zloader_t zl; - int ret = zonefile_open(&zl, zone_config->file, zone_config->name, - zone_config->enable_checks); + char *zonefile = conf_zonefile(conf, zone_name); + conf_val_t val = conf_zone_get(conf, C_SEM_CHECKS, zone_name); + int ret = zonefile_open(&zl, zonefile, zone_name, conf_bool(&val)); + free(zonefile); if (ret != KNOT_EOK) { return NULL; } @@ -39,7 +41,7 @@ zone_contents_t *zone_load_contents(conf_zone_t *zone_config) /* Set the zone type (master/slave). If zone has no master set, we * are the primary master for this zone (i.e. zone type = master). */ - zl.creator->master = !zone_load_can_bootstrap(zone_config); + zl.creator->master = !zone_load_can_bootstrap(conf, zone_name); zone_contents_t *zone_contents = zonefile_load(&zl); zonefile_close(&zl); @@ -51,7 +53,7 @@ zone_contents_t *zone_load_contents(conf_zone_t *zone_config) } /*! \brief Check zone configuration constraints. */ -int zone_load_check(zone_contents_t *contents, conf_zone_t *zone_config) +int zone_load_check(conf_t *conf, zone_contents_t *contents) { /* Bootstrapped zone, no checks apply. */ if (contents == NULL) { @@ -62,11 +64,12 @@ int zone_load_check(zone_contents_t *contents, conf_zone_t *zone_config) /* Check minimum EDNS0 payload if signed. (RFC4035/sec. 3) */ 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", - KNOT_EDNS_MIN_DNSSEC_PAYLOAD); - conf()->max_udp_payload = KNOT_EDNS_MIN_DNSSEC_PAYLOAD; + conf_val_t val = conf_get(conf, C_SRV, C_MAX_UDP_PAYLOAD); + if (conf_int(&val) < KNOT_EDNS_MIN_DNSSEC_PAYLOAD) { + log_zone_error(zone_name, "EDNS payload size is " + "lower than %u bytes for DNSSEC zone", + KNOT_EDNS_MIN_DNSSEC_PAYLOAD); + return KNOT_EPAYLOAD; } } @@ -84,11 +87,13 @@ int zone_load_check(zone_contents_t *contents, conf_zone_t *zone_config) /*! * \brief Apply changesets to zone from journal. */ -int zone_load_journal(zone_t *zone, zone_contents_t *contents) +int zone_load_journal(conf_t *conf, zone_t *zone, zone_contents_t *contents) { /* Check if journal is used and zone is not empty. */ - if (!journal_exists(zone->conf->ixfr_db) || + char *journal_name = conf_journalfile(conf, zone->name); + if (!journal_exists(journal_name) || zone_contents_is_empty(contents)) { + free(journal_name); return KNOT_EOK; } @@ -100,8 +105,10 @@ int zone_load_journal(zone_t *zone, zone_contents_t *contents) init_list(&chgs); pthread_mutex_lock(&zone->journal_lock); - int ret = journal_load_changesets(zone, &chgs, serial, serial - 1); + int ret = journal_load_changesets(journal_name, zone, &chgs, serial, + serial - 1); pthread_mutex_unlock(&zone->journal_lock); + free(journal_name); if ((ret != KNOT_EOK && ret != KNOT_ERANGE) || EMPTY_LIST(chgs)) { changesets_free(&chgs); @@ -124,14 +131,14 @@ int zone_load_journal(zone_t *zone, zone_contents_t *contents) return ret; } -int zone_load_post(zone_contents_t *contents, zone_t *zone, uint32_t *dnssec_refresh) +int zone_load_post(conf_t *conf, zone_contents_t *contents, zone_t *zone, + uint32_t *dnssec_refresh) { if (zone == NULL) { return KNOT_EINVAL; } int ret = KNOT_EOK; - const conf_zone_t *conf = zone->conf; changeset_t change; ret = changeset_init(&change, zone->name); if (ret != KNOT_EOK) { @@ -139,9 +146,12 @@ int zone_load_post(zone_contents_t *contents, zone_t *zone, uint32_t *dnssec_ref } /* Sign zone using DNSSEC (if configured). */ - if (conf->dnssec_enable) { - assert(conf->build_diffs); - ret = knot_dnssec_zone_sign(contents, conf, &change, 0, dnssec_refresh); + conf_val_t val = conf_zone_get(conf, C_DNSSEC_ENABLE, zone->name); + bool dnssec_enable = conf_bool(&val); + val = conf_zone_get(conf, C_IXFR_DIFF, zone->name); + bool build_diffs = conf_bool(&val); + if (dnssec_enable) { + ret = knot_dnssec_zone_sign(contents, &change, 0, dnssec_refresh); if (ret != KNOT_EOK) { changeset_clear(&change); return ret; @@ -160,9 +170,9 @@ int zone_load_post(zone_contents_t *contents, zone_t *zone, uint32_t *dnssec_ref } } - /* Calculate IXFR from differences (if configured). */ + /* Calculate IXFR from differences (if configured or auto DNSSEC). */ const bool contents_changed = zone->contents && (contents != zone->contents); - if (contents_changed && conf->build_diffs) { + if (contents_changed && (build_diffs || dnssec_enable)) { /* Replace changes from zone signing, the resulting diff will cover * those changes as well. */ changeset_clear(&change); @@ -197,7 +207,10 @@ int zone_load_post(zone_contents_t *contents, zone_t *zone, uint32_t *dnssec_ref return ret; } -bool zone_load_can_bootstrap(const conf_zone_t *zone_config) +bool zone_load_can_bootstrap(conf_t *conf, const knot_dname_t *zone_name) { - return zone_config && !EMPTY_LIST(zone_config->acl.xfr_in); + conf_val_t val = conf_zone_get(conf, C_MASTER, zone_name); + size_t count = conf_val_count(&val); + + return count > 0; } diff --git a/src/knot/zone/zone-load.h b/src/knot/zone/zone-load.h index dca148b9904d3e154badb81ebdcbf5e54fa35273..75020a1a174809261bc9336485b8352679b314be 100644 --- a/src/knot/zone/zone-load.h +++ b/src/knot/zone/zone-load.h @@ -24,40 +24,47 @@ /*! * \brief Load zone contents according to the configuration. * - * \param zone_config + * \param conf + * \param zone_name * \return new zone contents or NULL */ -zone_contents_t *zone_load_contents(conf_zone_t *zone_config); +zone_contents_t *zone_load_contents(conf_t *conf, const knot_dname_t *zone_name); /*! * \brief Check loaded zone contents validity. * + * \param conf * \param contents - * \param zone_config * \return KNOT_EOK or an error */ -int zone_load_check(zone_contents_t *contents, conf_zone_t *zone_config); +int zone_load_check(conf_t *conf, zone_contents_t *contents); /*! * \brief Update zone contents from the journal. * + * \param conf * \param zone * \param contents * \return KNOT_EOK or an error */ -int zone_load_journal(zone_t *zone, zone_contents_t *contents); +int zone_load_journal(conf_t *conf, zone_t *zone, zone_contents_t *contents); /*! * \brief Zone loading post-actions (zone resign, calculation of delta) * + * \param conf * \param contents * \param zone * \param dnssec_refresh * \return KNOT_EOK or an error */ -int zone_load_post(zone_contents_t *contents, zone_t *zone, uint32_t *dnssec_refresh); +int zone_load_post(conf_t *conf, zone_contents_t *contents, zone_t *zone, + uint32_t *dnssec_refresh); /*! * \brief Check if zone can be bootstrapped. + * + * \param conf + * \param zone_name */ -bool zone_load_can_bootstrap(const conf_zone_t *zone_config); +bool zone_load_can_bootstrap(conf_t *conf, const knot_dname_t *zone_name); diff --git a/src/knot/zone/zone.c b/src/knot/zone/zone.c index 58ec4179613c89cf8be85979e89cfd88382cf770..218f7e201becefe7eb64c339928b6200fdd50456 100644 --- a/src/knot/zone/zone.c +++ b/src/knot/zone/zone.c @@ -18,11 +18,13 @@ #include <assert.h> #include <string.h> #include <sys/stat.h> +#include <urcu.h> #include "dnssec/random.h" #include "libknot/descriptor.h" #include "knot/common/evsched.h" #include "libknot/internal/lists.h" +#include "knot/common/log.h" #include "knot/common/trim.h" #include "knot/zone/node.h" #include "knot/zone/serial.h" @@ -35,8 +37,11 @@ #include "libknot/libknot.h" #include "libknot/dname.h" #include "libknot/internal/utils.h" +#include "libknot/internal/mem.h" #include "libknot/rrtype/soa.h" +#define JOURNAL_SUFFIX ".diff.db" + static void free_ddns_queue(zone_t *z) { struct knot_request *n = NULL; @@ -49,28 +54,20 @@ static void free_ddns_queue(zone_t *z) } } -zone_t* zone_new(conf_zone_t *conf) +zone_t* zone_new(const knot_dname_t *name) { - if (!conf) { - return NULL; - } - zone_t *zone = malloc(sizeof(zone_t)); if (zone == NULL) { return NULL; } memset(zone, 0, sizeof(zone_t)); - zone->name = knot_dname_from_str_alloc(conf->name); - knot_dname_to_lower(zone->name); + zone->name = knot_dname_copy(name, NULL); if (zone->name == NULL) { free(zone); return NULL; } - // Configuration - zone->conf = conf; - // DDNS pthread_mutex_init(&zone->ddns_lock, NULL); zone->ddns_queue_size = 0; @@ -101,9 +98,6 @@ void zone_free(zone_t **zone_ptr) pthread_mutex_destroy(&zone->ddns_lock); pthread_mutex_destroy(&zone->journal_lock); - /* Free assigned config. */ - conf_free_zone(zone->conf); - /* Free zone contents. */ zone_contents_deep_free(&zone->contents); @@ -116,21 +110,25 @@ int zone_change_store(zone_t *zone, changeset_t *change) assert(zone); assert(change); - conf_zone_t *conf = zone->conf; + conf_val_t val = conf_zone_get(conf(), C_IXFR_FSLIMIT, zone->name); + int64_t ixfr_fslimit = conf_int(&val); + char *journal_file = conf_journalfile(conf(), zone->name); pthread_mutex_lock(&zone->journal_lock); - int ret = journal_store_changeset(change, conf->ixfr_db, conf->ixfr_fslimit); + int ret = journal_store_changeset(change, journal_file, ixfr_fslimit); if (ret == KNOT_EBUSY) { log_zone_notice(zone->name, "journal is full, flushing"); /* Transaction rolled back, journal released, we may flush. */ ret = zone_flush_journal(zone); if (ret == KNOT_EOK) { - ret = journal_store_changeset(change, conf->ixfr_db, conf->ixfr_fslimit); + ret = journal_store_changeset(change, journal_file, ixfr_fslimit); } } pthread_mutex_unlock(&zone->journal_lock); + free(journal_file); + return ret; } @@ -139,22 +137,26 @@ int zone_changes_store(zone_t *zone, list_t *chgs) assert(zone); assert(chgs); - conf_zone_t *conf = zone->conf; + conf_val_t val = conf_zone_get(conf(), C_IXFR_FSLIMIT, zone->name); + int64_t ixfr_fslimit = conf_int(&val); + char *journal_file = conf_journalfile(conf(), zone->name); pthread_mutex_lock(&zone->journal_lock); - int ret = journal_store_changesets(chgs, conf->ixfr_db, conf->ixfr_fslimit); - + int ret = journal_store_changesets(chgs, journal_file, ixfr_fslimit); if (ret == KNOT_EBUSY) { log_zone_notice(zone->name, "journal is full, flushing"); /* Transaction rolled back, journal released, we may flush. */ ret = zone_flush_journal(zone); if (ret == KNOT_EOK) { - ret = journal_store_changesets(chgs, conf->ixfr_db, conf->ixfr_fslimit); + ret = journal_store_changesets(chgs, journal_file, ixfr_fslimit); } + } pthread_mutex_unlock(&zone->journal_lock); + free(journal_file); + return ret; } @@ -171,29 +173,35 @@ zone_contents_t *zone_switch_contents(zone_t *zone, zone_contents_t *new_content return old_contents; } -const conf_iface_t *zone_master(const zone_t *zone) +bool zone_is_master(const zone_t *zone) { - if (zone == NULL) { - return NULL; - } + conf_val_t val = conf_zone_get(conf(), C_MASTER, zone->name); + return conf_val_count(&val) > 0 ? false : true; +} - if (EMPTY_LIST(zone->conf->acl.xfr_in)) { - return NULL; +conf_remote_t zone_master(const zone_t *zone) +{ + conf_val_t val = conf_zone_get(conf(), C_MASTER, zone->name); + + /* Seek the current master if possible. */ + if (zone->master_index < conf_val_count(&val)) { + for (size_t index = 0; index < zone->master_index; index++) { + conf_val_next(&val); + } } - conf_remote_t *master = HEAD(zone->conf->acl.xfr_in); - return master->remote; + return conf_remote(conf(), &val); } -void zone_master_rotate(const zone_t *zone) +void zone_master_rotate(zone_t *zone) { + conf_val_t val = conf_zone_get(conf(), C_MASTER, zone->name); - list_t *master_list = &zone->conf->acl.xfr_in; - if (list_size(master_list) < 2) { - return; + if (zone->master_index + 2 <= conf_val_count(&val)) { + zone->master_index += 1; + } else { + zone->master_index = 0; } - - add_tail(master_list, HEAD(*master_list)); } int zone_flush_journal(zone_t *zone) @@ -213,35 +221,44 @@ int zone_flush_journal(zone_t *zone) /* Fetch zone source (where it came from). */ const struct sockaddr_storage *from = NULL; - const conf_iface_t *master = zone_master(zone); - if (master != NULL) { - from = &master->addr; + if (!zone_is_master(zone)) { + const conf_remote_t master = zone_master(zone); + from = &master.addr; } + char *zonefile = conf_zonefile(conf(), zone->name); + /* Synchronize journal. */ - conf_zone_t *conf = zone->conf; - int ret = zonefile_write(conf->file, contents, from); + int ret = zonefile_write(zonefile, contents, from); if (ret == KNOT_EOK) { 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)", knot_strerror(ret)); + free(zonefile); return ret; } /* Update zone version. */ struct stat st; - if (stat(zone->conf->file, &st) < 0) { + if (stat(zonefile, &st) < 0) { log_zone_warning(zone->name, "failed to update zone file (%s)", knot_strerror(KNOT_EACCES)); + free(zonefile); return KNOT_EACCES; } + free(zonefile); + + char *journal_file = conf_journalfile(conf(), zone->name); + /* Update zone file serial and journal. */ zone->zonefile_mtime = st.st_mtime; zone->zonefile_serial = serial_to; - journal_mark_synced(zone->conf->ixfr_db); + journal_mark_synced(journal_file); + + free(journal_file); /* Trim extra heap. */ mem_trim(); diff --git a/src/knot/zone/zone.h b/src/knot/zone/zone.h index c7ce027d629f1bbf09d0845f8874a6ea9bc498ea..393b20164b1900b5c1fe6d2adaaf6e0f87cb5d1b 100644 --- a/src/knot/zone/zone.h +++ b/src/knot/zone/zone.h @@ -36,6 +36,7 @@ #include "knot/zone/events/events.h" #include "knot/zone/contents.h" #include "libknot/dname.h" +#include "libknot/packet/pkt.h" struct process_query_param; @@ -54,7 +55,6 @@ typedef struct zone { knot_dname_t *name; zone_contents_t *contents; - conf_zone_t *conf; zone_flag_t flags; /*! \brief DDNS queue and lock. */ @@ -71,6 +71,8 @@ typedef struct zone time_t zonefile_mtime; uint32_t zonefile_serial; + /*! \brief Config master list index of the current master server. */ + size_t master_index; } zone_t; /*----------------------------------------------------------------------------*/ @@ -78,11 +80,11 @@ typedef struct zone /*! * \brief Creates new zone with emtpy zone content. * - * \param conf Zone configuration. + * \param name Zone name. * * \return The initialized zone structure or NULL if an error occured. */ -zone_t *zone_new(conf_zone_t *conf); +zone_t* zone_new(const knot_dname_t *name); /*! * \brief Deallocates the zone structure. @@ -106,11 +108,14 @@ int zone_change_store(zone_t *zone, changeset_t *change); zone_contents_t *zone_switch_contents(zone_t *zone, zone_contents_t *new_contents); -/*! \brief Return zone master remote. */ -const conf_iface_t *zone_master(const zone_t *zone); +/*! \brief Check if the zone has some masters. */ +bool zone_is_master(const zone_t *zone); -/*! \brief Rotate list of master remotes for current zone. */ -void zone_master_rotate(const zone_t *zone); +/*! \brief Return the current zone master. */ +conf_remote_t zone_master(const zone_t *zone); + +/*! \brief Set the next zone master as a current. */ +void zone_master_rotate(zone_t *zone); /*! \brief Synchronize zone file with journal. */ int zone_flush_journal(zone_t *zone); diff --git a/src/knot/zone/zonedb-load.c b/src/knot/zone/zonedb-load.c index b9737f8e62125c2df0b9b2f910ec563da890bf02..45844864936849b669496108e444354519302b6c 100644 --- a/src/knot/zone/zonedb-load.c +++ b/src/knot/zone/zonedb-load.c @@ -16,18 +16,18 @@ #include <assert.h> #include <sys/stat.h> +#include <urcu.h> -#include "libknot/libknot.h" #include "knot/zone/zonedb-load.h" #include "knot/zone/zone-load.h" #include "knot/conf/conf.h" -#include "libknot/rrtype/soa.h" #include "knot/zone/zone.h" #include "knot/zone/zonefile.h" #include "knot/zone/zonedb.h" #include "knot/zone/timers.h" #include "knot/server/server.h" -#include "libknot/dname.h" +#include "knot/common/log.h" +#include "libknot/libknot.h" /*- zone file status --------------------------------------------------------*/ @@ -50,12 +50,15 @@ typedef enum { * * \return Zone status. */ -static zone_status_t zone_file_status(const zone_t *old_zone, - const conf_zone_t *conf) +static zone_status_t zone_file_status(const zone_t *old_zone, conf_t *conf, + const knot_dname_t *name) { assert(conf); + assert(name); - time_t mtime = zonefile_mtime(conf->file); + char *zonefile = conf_zonefile(conf, name); + time_t mtime = zonefile_mtime(zonefile); + free(zonefile); if (mtime < 0) { // Zone file does not exist. @@ -63,8 +66,8 @@ static zone_status_t zone_file_status(const zone_t *old_zone, // Deferred flush. return ZONE_STATUS_FOUND_CURRENT; } else { - return zone_load_can_bootstrap(conf) ? ZONE_STATUS_BOOSTRAP \ - : ZONE_STATUS_NOT_FOUND; + return zone_load_can_bootstrap(conf, name) ? + ZONE_STATUS_BOOSTRAP : ZONE_STATUS_NOT_FOUND; } } else { // Zone file exists. @@ -84,14 +87,11 @@ static zone_status_t zone_file_status(const zone_t *old_zone, * \brief Log message about loaded zone (name and status). * * \param zone Zone structure. - * \param zone_name Printable name of the zone. * \param status Zone file status. */ -static void log_zone_load_info(const zone_t *zone, const char *zone_name, - zone_status_t status) +static void log_zone_load_info(const zone_t *zone, zone_status_t status) { assert(zone); - assert(zone_name); const char *action = NULL; @@ -114,9 +114,9 @@ static void log_zone_load_info(const zone_t *zone, const char *zone_name, log_zone_info(zone->name, "zone %s, serial %u", action, serial); } -static zone_t *create_zone_from(conf_zone_t *zone_conf, server_t *server) +static zone_t *create_zone_from(const knot_dname_t *name, server_t *server) { - zone_t *zone = zone_new(zone_conf); + zone_t *zone = zone_new(name); if (!zone) { return NULL; } @@ -124,7 +124,6 @@ static zone_t *create_zone_from(conf_zone_t *zone_conf, server_t *server) int result = zone_events_setup(zone, server->workers, &server->sched, server->timers_db); if (result != KNOT_EOK) { - zone->conf = NULL; zone_free(&zone); return NULL; } @@ -132,16 +131,16 @@ static zone_t *create_zone_from(conf_zone_t *zone_conf, server_t *server) return zone; } -static zone_t *create_zone_reload(conf_zone_t *zone_conf, server_t *server, - zone_t *old_zone) +static zone_t *create_zone_reload(conf_t *conf, const knot_dname_t *name, + server_t *server, zone_t *old_zone) { - zone_t *zone = create_zone_from(zone_conf, server); + zone_t *zone = create_zone_from(name, server); if (!zone) { return NULL; } zone->contents = old_zone->contents; - const zone_status_t zstatus = zone_file_status(old_zone, zone_conf); + const zone_status_t zstatus = zone_file_status(old_zone, conf, name); switch (zstatus) { case ZONE_STATUS_FOUND_UPDATED: @@ -177,7 +176,7 @@ static void reuse_events(zone_t *zone, const time_t *timers) // Timer unset. continue; } - if (slave_event(event) && !zone_master(zone)) { + if (slave_event(event) && zone_is_master(zone)) { // Slave-only event. continue; } @@ -192,9 +191,10 @@ static bool zone_expired(const time_t *timers) return now <= timers[ZONE_EVENT_EXPIRE]; } -static zone_t *create_zone_new(conf_zone_t *zone_conf, server_t *server) +static zone_t *create_zone_new(conf_t * conf, const knot_dname_t *name, + server_t *server) { - zone_t *zone = create_zone_from(zone_conf, server); + zone_t *zone = create_zone_from(name, server); if (!zone) { return NULL; } @@ -207,14 +207,13 @@ static zone_t *create_zone_new(conf_zone_t *zone_conf, server_t *server) if (ret != KNOT_EOK) { log_zone_error(zone->name, "cannot read zone timers (%s)", knot_strerror(ret)); - zone->conf = NULL; zone_free(&zone); return NULL; } reuse_events(zone, timers); - const zone_status_t zstatus = zone_file_status(NULL, zone_conf); + const zone_status_t zstatus = zone_file_status(NULL, conf, name); switch (zstatus) { case ZONE_STATUS_FOUND_NEW: @@ -235,7 +234,7 @@ static zone_t *create_zone_new(conf_zone_t *zone_conf, server_t *server) assert(0); } - log_zone_load_info(zone, zone_conf->name, zstatus); + log_zone_load_info(zone, zstatus); return zone; } @@ -243,22 +242,23 @@ static zone_t *create_zone_new(conf_zone_t *zone_conf, server_t *server) /*! * \brief Load or reload the zone. * - * \param zone_conf Zone configuration. + * \param conf Configuration. * \param server Server. * \param old_zone Already loaded zone (can be NULL). * * \return Error code, KNOT_EOK if successful. */ -static zone_t *create_zone(conf_zone_t *zone_conf, server_t *server, +static zone_t *create_zone(conf_t *conf, const knot_dname_t *name, server_t *server, zone_t *old_zone) { - assert(zone_conf); + assert(conf); + assert(name); assert(server); if (old_zone) { - return create_zone_reload(zone_conf, server, old_zone); + return create_zone_reload(conf, name, server, old_zone); } else { - return create_zone_new(zone_conf, server); + return create_zone_new(conf, name, server); } } @@ -273,37 +273,31 @@ static zone_t *create_zone(conf_zone_t *zone_conf, server_t *server, * * \return New zone database. */ -static knot_zonedb_t *create_zonedb(const conf_t *conf, server_t *server) +static knot_zonedb_t *create_zonedb(conf_t *conf, server_t *server) { assert(conf); assert(server); knot_zonedb_t *db_old = server->zone_db; - knot_zonedb_t *db_new = knot_zonedb_new(hattrie_weight(conf->zones)); + knot_zonedb_t *db_new = knot_zonedb_new(conf_id_count(conf, C_ZONE)); if (!db_new) { return NULL; } - hattrie_iter_t *it = hattrie_iter_begin(conf->zones, false); - for (; !hattrie_iter_finished(it); hattrie_iter_next(it)) { - - conf_zone_t *zone_config = (conf_zone_t *)*hattrie_iter_val(it); - - knot_dname_t *apex = knot_dname_from_str_alloc(zone_config->name); - zone_t *old_zone = knot_zonedb_find(db_old, apex); - knot_dname_free(&apex, NULL); - - zone_t *zone = create_zone(zone_config, server, old_zone); + conf_iter_t iter = conf_iter(conf, C_ZONE); + while (iter.code == KNOT_EOK) { + conf_val_t id = conf_iter_id(conf, &iter); + zone_t *old_zone = knot_zonedb_find(db_old, conf_dname(&id)); + zone_t *zone = create_zone(conf, conf_dname(&id), server, old_zone); if (!zone) { - log_zone_str_error(zone_config->name, - "zone cannot be created"); - conf_free_zone(zone_config); + log_zone_error(id.data, "zone cannot be created"); continue; } - knot_zonedb_insert(db_new, zone); + + conf_iter_next(conf, &iter); } - hattrie_iter_free(it); + conf_iter_finish(conf, &iter); return db_new; } @@ -347,7 +341,7 @@ static int remove_old_zonedb(const knot_zonedb_t *db_new, knot_zonedb_t *db_old) /*! * \brief Update zone database according to configuration. */ -int zonedb_reload(const conf_t *conf, struct server *server) +int zonedb_reload(conf_t *conf, struct server *server) { /* Check parameters */ if (conf == NULL || server == NULL) { diff --git a/src/knot/zone/zonedb-load.h b/src/knot/zone/zonedb-load.h index 03ad505a8db9989a2d82482ea90140a8d18f0df6..af83bb0d09020e4411569c88b178952ff318c454 100644 --- a/src/knot/zone/zonedb-load.h +++ b/src/knot/zone/zonedb-load.h @@ -31,4 +31,4 @@ struct server; * \retval KNOT_EINVAL * \retval KNOT_ERROR */ -int zonedb_reload(const conf_t *conf, struct server *server); +int zonedb_reload(conf_t *conf, struct server *server); diff --git a/src/knot/zone/zonefile.c b/src/knot/zone/zonefile.c index 27115258c64d3394f4e19864ee00a99a16325180..caf5c84d5cd099751215886622a8a3614b0828a9 100644 --- a/src/knot/zone/zonefile.c +++ b/src/knot/zone/zonefile.c @@ -187,23 +187,8 @@ static void scanner_process(zs_scanner_t *scanner) knot_rdataset_clear(&rr.rrs, NULL); } -static zone_contents_t *create_zone_from_name(const char *origin) -{ - if (origin == NULL) { - return NULL; - } - knot_dname_t *owner = knot_dname_from_str_alloc(origin); - if (owner == NULL) { - return NULL; - } - knot_dname_to_lower(owner); - zone_contents_t *z = zone_contents_new(owner); - knot_dname_free(&owner, NULL); - return z; -} - -int zonefile_open(zloader_t *loader, const char *source, const char *origin, - bool semantic_checks) +int zonefile_open(zloader_t *loader, const char *source, + const knot_dname_t *origin, bool semantic_checks) { if (!loader) { return KNOT_EINVAL; @@ -221,24 +206,32 @@ int zonefile_open(zloader_t *loader, const char *source, const char *origin, } memset(zc, 0, sizeof(zcreator_t)); - zc->z = create_zone_from_name(origin); + zc->z = zone_contents_new(origin); if (zc->z == NULL) { free(zc); return KNOT_ENOMEM; } + /* Prepare textual owner for zone scanner. */ + char *origin_str = knot_dname_to_str_alloc(origin); + if (origin_str == NULL) { + free(zc); + return KNOT_ENOMEM; + } + /* Create file loader. */ memset(loader, 0, sizeof(zloader_t)); - loader->scanner = zs_scanner_create(origin, KNOT_CLASS_IN, 3600, + loader->scanner = zs_scanner_create(origin_str, KNOT_CLASS_IN, 3600, scanner_process, process_error, zc); if (loader->scanner == NULL) { + free(origin_str); free(zc); return KNOT_ERROR; } loader->source = strdup(source); - loader->origin = strdup(origin); + loader->origin = origin_str; loader->creator = zc; loader->semantic_checks = semantic_checks; diff --git a/src/knot/zone/zonefile.h b/src/knot/zone/zonefile.h index 2834a20cd4ea6e57d5dc6d09a78b74a4150187bb..d181f8fd6d92a715dcc2b5af52a1e3d4014a733d 100644 --- a/src/knot/zone/zonefile.h +++ b/src/knot/zone/zonefile.h @@ -65,8 +65,8 @@ typedef struct zloader { * \retval Initialized loader on success. * \retval NULL on error. */ -int zonefile_open(zloader_t *loader, const char *source, const char *origin, - bool semantic_checks); +int zonefile_open(zloader_t *loader, const char *source, + const knot_dname_t *origin, bool semantic_checks); /*! * \brief Loads zone from a zone file. diff --git a/src/libknot/errcode.c b/src/libknot/errcode.c index df9f908f9f1203a0856081f9adf5148d4dd4b27c..5bb51dd2648dd6593d3bf5604b3ed9248521d5a5 100644 --- a/src/libknot/errcode.c +++ b/src/libknot/errcode.c @@ -69,7 +69,7 @@ static const struct error errors[] = { { 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_EPAYLOAD, "invalid EDNS payload size" }, { KNOT_EPREREQ, "UPDATE prerequisity not met" }, { KNOT_ETTL, "TTL mismatch" }, { KNOT_ENOXFR, "transfer was not sent" }, diff --git a/src/libknot/rrtype/tsig.c b/src/libknot/rrtype/tsig.c index fe71ca5cc2069585ba6d74cdb671f87f26c1f35a..9994189f769a9e7cd0b8ec1a1d09f03a7c45bc81 100644 --- a/src/libknot/rrtype/tsig.c +++ b/src/libknot/rrtype/tsig.c @@ -358,7 +358,7 @@ size_t knot_tsig_rdata_tsig_timers_length() _public_ size_t knot_tsig_wire_maxsize(const knot_tsig_key_t *key) { - if (key == NULL) { + if (key == NULL || key->name == NULL) { return 0; } diff --git a/src/libknot/rrtype/tsig.h b/src/libknot/rrtype/tsig.h index e83feee366cdc10b18a3a184d7bd762ad92ebb91..4464e582766640b54db2688d5841d2c3c1ef9b4c 100644 --- a/src/libknot/rrtype/tsig.h +++ b/src/libknot/rrtype/tsig.h @@ -58,7 +58,7 @@ enum tsig_consts { /*! \brief Packet signing context. * \todo This should be later moved to TSIG files when refactoring. */ typedef struct knot_sign_context { - knot_tsig_key_t *tsig_key; + knot_tsig_key_t tsig_key; uint8_t *tsig_buf; uint8_t *tsig_digest; size_t tsig_buflen; diff --git a/src/libknot/yparser/ypformat.c b/src/libknot/yparser/ypformat.c index 2dd97352d8b1e0878bcb887d36838c5d25072635..8e88e4daeb1e6c947cb1d4dc999fc805f2114899 100644 --- a/src/libknot/yparser/ypformat.c +++ b/src/libknot/yparser/ypformat.c @@ -14,6 +14,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "libknot/internal/macros.h" #include "libknot/yparser/yptrafo.h" #include "libknot/libknot.h" @@ -73,6 +74,7 @@ static int format_item( return KNOT_EOK; } +_public_ int yp_format_key0( const yp_item_t *item, const uint8_t *data, @@ -87,6 +89,7 @@ int yp_format_key0( first_value, last_value); } +_public_ int yp_format_id( const yp_item_t *item, const uint8_t *data, @@ -103,6 +106,7 @@ int yp_format_id( true, true); } +_public_ int yp_format_key1( const yp_item_t *item, const uint8_t *data, diff --git a/tests-extra/tests/events/soa/test.py b/tests-extra/tests/events/soa/test.py index e8b35ae79913415ae36b158537a060aa696d93c2..807bda81c2b5380107aacb27367d19ef067cc8b6 100644 --- a/tests-extra/tests/events/soa/test.py +++ b/tests-extra/tests/events/soa/test.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 -'''Test for SOA events and planning thereof''' +'''Test for SOA events and planning thereof without NOTIFY''' from dnstest.utils import * from dnstest.test import Test import random -EXPIRE_SLEEP = 4 +EXPIRE_SLEEP = 6 def test_refresh(slave): resp = slave.dig("example.", "SOA") @@ -14,7 +14,7 @@ def test_refresh(slave): t.sleep(EXPIRE_SLEEP) resp = slave.dig("example.", "SOA") resp.check(rcode="NOERROR") - + def test_expire(slave): resp = slave.dig("example.", "SOA") resp.check(rcode="NOERROR") @@ -27,6 +27,7 @@ def create_servers(t): for _ in range(3): master = t.server("bind") master.disable_notify = True + master.max_conn_idle = "1s" slave = t.server("knot") slave.disable_notify = True @@ -112,14 +113,14 @@ t = Test() random.seed() -# this zone has refresh = 1s, retry = 1s and expire = 1s + 2s for connection timeouts +# This zone has refresh = 1s, retry = 1s and expire = 2s zone = t.zone("example.", storage=".") servers = create_servers(t) t.start() -#stop the servers so that the zone does not expire +# Stop the servers so that the zone does not expire for server_pair in servers: server_pair[0].stop() server_pair[1].stop() diff --git a/tests-extra/tests/zone/expire/test.py b/tests-extra/tests/zone/expire/test.py index 4a1469bbe7708f576068286ad007637007486131..164eafa878031e9f9681502a42e19f781e44aab8 100644 --- a/tests-extra/tests/zone/expire/test.py +++ b/tests-extra/tests/zone/expire/test.py @@ -13,12 +13,12 @@ def break_xfrout(server): config = f.read() f.seek(0) f.truncate() - config = config.replace("xfr-out ", "#xfr-out ") + config = config.replace(" acl:", " #acl:") f.write(config) t = Test(tsig=False) -# this zone has refresh = 1s, retry = 1s and expire = 10s + 2s for connection timeouts +# this zone has refresh = 1s, retry = 1s and expire = 8s zone = t.zone("example.", storage=".") EXPIRE_SLEEP = 15 diff --git a/tests-extra/tests/zone/timers/test.py b/tests-extra/tests/zone/timers/test.py index b11740bafe90f60ebc1667ac2ec5f540fa32b1da..01f39c0ad5434059ff9cb6ee57a4634da1c7c094 100644 --- a/tests-extra/tests/zone/timers/test.py +++ b/tests-extra/tests/zone/timers/test.py @@ -5,7 +5,7 @@ from dnstest.test import Test import random -EXPIRE_SLEEP = 17 +EXPIRE_SLEEP = 20 RETRY_SLEEP = 10 START_SLEEP = 5 @@ -104,7 +104,7 @@ def refresh_tests(t, zone, master, slave): t = Test() -# this zone has refresh = 1s, retry = 1s and expire = 1s + 2s for connection timeouts +# this zone has refresh = 1s, retry = 1s and expire = 16s zone = t.zone("example.", storage=".") master = t.server("knot") diff --git a/tests-extra/tools/dnstest/server.py b/tests-extra/tools/dnstest/server.py index 62a4c309be617f2aed585841260f054ebc006ae4..352d28784706d41926e7e37a083fa8c974fccd30 100644 --- a/tests-extra/tools/dnstest/server.py +++ b/tests-extra/tools/dnstest/server.py @@ -33,29 +33,27 @@ class KnotConf(object): def __init__(self): self.conf = "" - self.indent = "" - - def sub(self): - self.indent += "\t" - - def unsub(self): - self.indent = self.indent[:-1] + self.first_item = True def begin(self, name): - self.conf += "%s%s {\n" % (self.indent, name) - self.sub() + self.conf += "%s:\n" % name + self.first_item = True def end(self): - self.unsub() - self.conf += "%s}\n" % (self.indent) - if not self.indent: - self.conf += "\n" + self.conf += "\n" def item(self, name, value): - self.conf += "%s%s %s;\n" % (self.indent, name, value) + self.conf += " %s: %s\n" % (name, value) def item_str(self, name, value): - self.conf += "%s%s \"%s\";\n" % (self.indent, name, value) + self.conf += " %s: \"%s\"\n" % (name, value) + + def id_item(self, name, value): + if not self.first_item: + self.conf += "\n" + else: + self.first_item = False + self.conf += " - %s: \"%s\"\n" % (name, value) class BindConf(object): '''Bind server config generator''' @@ -775,11 +773,13 @@ class Bind(Server): s.item("type", "slave") if self.tsig: - s.item("allow-notify", "{ key %s; }" % z.master.tsig.name) + if not z.master.disable_notify: + s.item("allow-notify", "{ key %s; }" % z.master.tsig.name) s.item("masters", "{ %s port %i key %s; }" \ % (z.master.addr, z.master.port, z.master.tsig.name)) else: - s.item("allow-notify", "{ %s; }" % z.master.addr) + if not z.master.disable_notify: + s.item("allow-notify", "{ %s; }" % z.master.addr) s.item("masters", "{ %s port %i; }" \ % (z.master.addr, z.master.port)) else: @@ -792,12 +792,15 @@ class Bind(Server): if z.slaves: slaves = "" for slave in z.slaves: + if slave.disable_notify: + continue if self.tsig: slaves += "%s port %i key %s; " \ % (slave.addr, slave.port, self.tsig.name) else: slaves += "%s port %i; " % (slave.addr, slave.port) - s.item("also-notify", "{ %s}" % slaves) + if slaves: + s.item("also-notify", "{ %s}" % slaves) if z.ddns: if self.tsig: @@ -844,154 +847,174 @@ class Knot(Server): def _on_str_hex(self, conf, name, value): if value == True: - conf.item(name, "on") + conf.item_str(name, "") elif value == False: - conf.item(name, "off") + return elif value: - if isinstance(value, int) or value[:2] == "0x": - conf.item(name, value) - else: - conf.item_str(name, value) + conf.item_str(name, value) + + def _key(self, conf, key): + conf.id_item("id", key.name) + conf.item_str("algorithm", key.alg) + conf.item_str("secret", key.key) def get_config(self): s = KnotConf() - s.begin("system") + s.begin("server") self._on_str_hex(s, "identity", self.ident) self._on_str_hex(s, "version", self.version) self._on_str_hex(s, "nsid", self.nsid) - self._on_str_hex(s, "rate-limit", self.ratelimit) s.item_str("rundir", self.dir) + s.item_str("listen", "%s@%s" % (self.addr, self.port)) if (self.max_conn_idle): - s.item("max-conn-idle", self.max_conn_idle) + s.item_str("max-conn-idle", self.max_conn_idle) + if (self.ratelimit): + s.item_str("rate-limit", self.ratelimit) s.end() s.begin("control") - s.item_str("listen-on", "knot.sock") - s.end() - - s.begin("interfaces") - if self.ip == 4: - s.begin("ipv4") - else: - s.begin("ipv6") - s.item("address", self.addr) - s.item("port", self.port) - s.end() + s.item_str("listen", "knot.sock") s.end() if self.tsig: - s.begin("keys") - t = self.tsig - s.item_str("\"%s\" %s" % (t.name, t.alg), t.key) - t = self.tsig_test - s.item_str("\"%s\" %s" % (t.name, t.alg), t.key) + s.begin("key") + self._key(s, self.tsig) + self._key(s, self.tsig_test) keys = set() # Duplicy check. for zone in sorted(self.zones): z = self.zones[zone] if z.master and z.master.tsig.name not in keys: t = z.master.tsig - s.item_str("\"%s\" %s" % (t.name, t.alg), t.key) + self._key(s, t) keys.add(t.name) for slave in z.slaves: if slave.tsig and slave.tsig.name not in keys: t = slave.tsig - s.item_str("\"%s\" %s" % (t.name, t.alg), t.key) + self._key(s, t) keys.add(t.name) s.end() - s.begin("remotes") - s.begin("local") - s.item("address", self.addr) + have_remote = False + servers = set() # Duplicity check. + for zone in sorted(self.zones): + z = self.zones[zone] + if z.master and z.master.name not in servers: + if not have_remote: + s.begin("remote") + have_remote = True + s.id_item("id", z.master.name) + s.item_str("address", "%s@%s" % (z.master.addr, z.master.port)) + if z.master.tsig: + s.item_str("key", z.master.tsig.name) + servers.add(z.master.name) + for slave in z.slaves: + if slave.name not in servers: + if not have_remote: + s.begin("remote") + have_remote = True + s.id_item("id", slave.name) + s.item_str("address", "%s@%s" % (slave.addr, slave.port)) + if slave.tsig: + s.item_str("key", slave.tsig.name) + servers.add(slave.name) + if have_remote: + s.end() + + s.begin("acl") + s.id_item("id", "acl_local") + s.item_str("address", self.addr) if self.tsig: s.item_str("key", self.tsig.name) - s.end() - s.begin("test") - s.item("address", self.addr) + s.item("action", "[xfer, notify, update]") + + s.id_item("id", "acl_test") + s.item_str("address", self.addr) if self.tsig_test: s.item_str("key", self.tsig_test.name) - s.end() + s.item("action", "[xfer, notify, update]") servers = set() # Duplicity check. for zone in sorted(self.zones): z = self.zones[zone] if z.master and z.master.name not in servers: - s.begin(z.master.name) - s.item("address", z.master.addr) - s.item("port", z.master.port) + s.id_item("id", "acl_%s" % z.master.name) + s.item_str("address", z.master.addr) if z.master.tsig: s.item_str("key", z.master.tsig.name) - s.end() + s.item("action", "notify") servers.add(z.master.name) for slave in z.slaves: - if slave.name not in servers: - s.begin(slave.name) - s.item("address", slave.addr) - s.item("port", slave.port) - if slave.tsig: - s.item_str("key", self.tsig.name) - s.end() - servers.add(slave.name) + if slave.name in servers: + continue + s.id_item("id", "acl_%s" % slave.name) + s.item_str("address", slave.addr) + if slave.tsig: + s.item_str("key", slave.tsig.name) + s.item("action", "xfer") + servers.add(slave.name) s.end() - s.begin("zones") + s.begin("template") + s.id_item("id", "default") s.item_str("storage", self.dir) if self.zonefile_sync: - s.item("zonefile-sync", self.zonefile_sync) + s.item_str("zonefile-sync", self.zonefile_sync) else: - s.item("zonefile-sync", "1d") + s.item_str("zonefile-sync", "1d") if self.ixfr_fslimit: - s.item("ixfr-fslimit", self.ixfr_fslimit) - s.item("notify-timeout", "5") - s.item("notify-retries", "5") - s.item("semantic-checks", "on") + s.item_str("ixfr-fslimit", self.ixfr_fslimit) + s.item_str("notify-timeout", "5") + s.item_str("notify-retries", "5") + s.item_str("semantic-checks", "on") if self.disable_any: - s.item("disable-any", "on") + s.item_str("disable-any", "on") if self.dnssec_enable: s.item_str("dnssec-keydir", self.keydir) - s.item("dnssec-enable", "on") + s.item_str("dnssec-enable", "on") + s.end() + + s.begin("zone") for zone in sorted(self.zones): z = self.zones[zone] - s.begin(z.name) + s.id_item("domain", z.name) s.item_str("file", z.zfile.path) + acl = "" if z.master: - if not self.disable_notify: - s.item("notify-in", z.master.name) - s.item("xfr-in", z.master.name) + s.item("master", z.master.name) + if not z.master.disable_notify: + acl = "acl_%s" % z.master.name - slaves = "" if z.slaves: + slaves = "" for slave in z.slaves: + if slave.disable_notify: + continue if slaves: slaves += ", " slaves += slave.name - s.item("notify-out", slaves) - - s.item("xfr-out", "local, test") + if slaves: + s.item("notify", "[%s]" % slaves) - if z.ddns: - s.item("update-in", "test") + if acl: + acl += ", " + acl += "acl_local, acl_test" + s.item("acl", "[%s]" % acl) if z.ixfr and not z.master: - s.item("ixfr-from-differences", "on") + s.item_str("ixfr-from-differences", "on") if len(z.query_modules) > 0: s.begin("query_module") for query_module in z.query_modules: - s.item(query_module[0], '"' + query_module[1] + '"') + s.item_str(query_module[0], '"' + query_module[1] + '"') s.end() - s.end() s.end() s.begin("log") - s.begin("stdout") - s.item("any", "debug") - s.end() - s.begin("stderr") - s.end() - s.begin("syslog") - s.end() + s.id_item("to", "stdout") + s.item_str("any", "debug") s.end() self.start_params = ["-c", self.confile] diff --git a/tests/Makefile.am b/tests/Makefile.am index 497bc4fe7f719581294f40423fff7f0d0b4bc228..7af9b856994fa0adf6f4d7899e112cabbff089bf 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -10,15 +10,14 @@ AM_LDFLAGS = \ LDADD = \ $(top_builddir)/libtap/libtap.a \ $(top_builddir)/src/libknot-int.la \ - $(top_builddir)/src/libknot-yparser.la \ - $(top_builddir)/src/libknotd.la + $(top_builddir)/src/libknotd.la \ + $(top_builddir)/src/libknot-yparser.la check_PROGRAMS = \ acl \ base32hex \ base64 \ changeset \ - changeset \ descriptor \ dname \ dthreads \ diff --git a/tests/acl.c b/tests/acl.c index e2bebd62d5dfaff5965487d7eed82c89a58466ea..5df8959613924766f61db0985c4953feb75a0b87 100644 --- a/tests/acl.c +++ b/tests/acl.c @@ -14,6 +14,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <assert.h> #include <sys/types.h> #include <sys/socket.h> #include <tap/basic.h> @@ -23,127 +24,78 @@ #include "knot/updates/acl.h" #include "knot/conf/conf.h" -static int acl_insert(list_t *acl, const struct sockaddr_storage *addr, - uint8_t prefix, knot_tsig_key_t *key) -{ - assert(acl); - assert(addr); - - conf_iface_t *iface = malloc(sizeof(conf_iface_t)); - assert(iface); - conf_remote_t *remote = malloc(sizeof(conf_remote_t)); - assert(remote); - remote->remote = iface; - - memset(iface, 0, sizeof(conf_iface_t)); - iface->prefix = prefix; - iface->key = key; - memcpy(&iface->addr, addr, sizeof(struct sockaddr_storage)); - - add_tail(acl, &remote->n); - return KNOT_EOK; -} - int main(int argc, char *argv[]) { - plan(15); - - conf_iface_t *match = NULL; - list_t acl; - init_list(&acl); - - // Create IPv4 address - struct sockaddr_storage test_v4; - int ret = sockaddr_set(&test_v4, AF_INET, "127.0.0.1", 12345); - ok(ret == KNOT_EOK, "acl: new IPv4 address"); - - // Create IPv6 address - struct sockaddr_storage test_v6; - ret = sockaddr_set(&test_v6, AF_INET6, "::1", 54321); - ok(ret == KNOT_EOK, "acl: new IPv6 address"); - - // Create simple IPv4 rule - ret = acl_insert(&acl, &test_v4, IPV4_PREFIXLEN, NULL); - ok(ret == KNOT_EOK, "acl: inserted IPv4 rule"); - - // Create simple IPv6 rule - ret = acl_insert(&acl, &test_v6, IPV6_PREFIXLEN, NULL); - ok(ret == KNOT_EOK, "acl: inserted IPv6 rule"); - - // Attempt to match unmatching address - struct sockaddr_storage unmatch_v4; - sockaddr_set(&unmatch_v4, AF_INET, "10.10.10.10", 24424); - match = acl_find(&acl, &unmatch_v4, NULL); - ok(match == NULL, "acl: matching non-existing address"); - - // Attempt to match unmatching IPv6 address - struct sockaddr_storage unmatch_v6; - sockaddr_set(&unmatch_v6, AF_INET6, "2001:db8::1428:57ab", 24424); - match = acl_find(&acl, &unmatch_v6, NULL); - ok(match == NULL, "acl: matching non-existing IPv6 address"); - - // Attempt to match matching address - match = acl_find(&acl, &test_v4, NULL); - ok(match != NULL, "acl: matching existing address"); - - // Attempt to match matching address - match = acl_find(&acl, &test_v6, NULL); - ok(match != NULL, "acl: matching existing IPv6 address"); - - // Attempt to match subnet - struct sockaddr_storage match_pf4, test_pf4; - sockaddr_set(&match_pf4, AF_INET, "192.168.1.0", 0); - acl_insert(&acl, &match_pf4, 24, NULL); - sockaddr_set(&test_pf4, AF_INET, "192.168.1.20", 0); - match = acl_find(&acl, &test_pf4, NULL); - ok(match != NULL, "acl: searching address in matching prefix /24"); - - // Attempt to search non-matching subnet - sockaddr_set(&test_pf4, AF_INET, "192.168.2.20", 0); - match = acl_find(&acl, &test_pf4, NULL); - ok(match == NULL, "acl: searching address in non-matching prefix /24"); - - // Attempt to match v6 subnet - struct sockaddr_storage match_pf6, test_pf6; - sockaddr_set(&match_pf6, AF_INET6, "2001:0DB8:0400:000e:0:0:0:AB00", 0); - acl_insert(&acl, &match_pf6, 120, NULL); - sockaddr_set(&test_pf6, AF_INET6, "2001:0DB8:0400:000e:0:0:0:AB03", 0); - match = acl_find(&acl, &test_pf6, NULL); - ok(match != NULL, "acl: searching v6 address in matching prefix /120"); - - // Attempt to search non-matching subnet - sockaddr_set(&test_pf6, AF_INET6, "2001:0DB8:0400:000e:0:0:0:CCCC", 0); - match = acl_find(&acl, &test_pf6, NULL); - ok(match == NULL, "acl: searching v6 address in non-matching prefix /120"); - - // Attempt to search subnet with key (multiple keys) - knot_tsig_key_t key_a, key_b; - knot_tsig_create_key("tsig-key1", DNSSEC_TSIG_HMAC_MD5, "Wg==", &key_a); - knot_tsig_create_key("tsig-key2", DNSSEC_TSIG_HMAC_MD5, "Wg==", &key_b); - acl_insert(&acl, &match_pf6, 120, &key_a); - acl_insert(&acl, &match_pf6, 120, &key_b); - sockaddr_set(&test_pf6, AF_INET6, "2001:0DB8:0400:000e:0:0:0:AB03", 0); - match = acl_find(&acl, &test_pf6, key_a.name); - ok(match != NULL && match->key == &key_a, "acl: searching v6 address with TSIG key A"); - match = acl_find(&acl, &test_pf6, key_b.name); - ok(match != NULL && match->key == &key_b, "acl: searching v6 address with TSIG key B"); - - // Attempt to search subnet with mismatching key - knot_tsig_key_t badkey; - knot_tsig_create_key("tsig-bad", DNSSEC_TSIG_HMAC_MD5, "Wg==", &badkey); - match = acl_find(&acl, &test_pf6, badkey.name); - ok(match == NULL, "acl: searching v6 address with bad TSIG key"); - knot_tsig_key_free(&badkey); - - knot_tsig_key_free(&key_a); - knot_tsig_key_free(&key_b); - - conf_remote_t *remote = NULL, *next = NULL; - WALK_LIST_DELSAFE(remote, next, acl) { - free(remote->remote); - free(remote); - } - - // Return + plan_lazy(); + + int ret; + struct sockaddr_storage t; + + // 127 dec ~ 01111111 bin + // 170 dec ~ 10101010 bin + struct sockaddr_storage ref4; + assert(sockaddr_set(&ref4, AF_INET, "127.170.170.127", 0) == KNOT_EOK); + + // 7F hex ~ 01111111 bin + // AA hex ~ 10101010 bin + struct sockaddr_storage ref6; + assert(sockaddr_set(&ref6, AF_INET6, "7FAA::AA7F", 0) == KNOT_EOK); + + ret = netblock_match(&ref4, &ref6, 32); + ok(ret == false, "match: family mismatch"); + + ret = netblock_match(NULL, &ref4, 32); + ok(ret == false, "match: NULL first parameter"); + ret = netblock_match(&ref4, NULL, 32); + ok(ret == false, "match: NULL second parameter"); + + ret = netblock_match(&ref4, &ref4, 31); + ok(ret == true, "match: ipv4 - identity, subnet"); + ret = netblock_match(&ref4, &ref4, 32); + ok(ret == true, "match: ipv4 - identity, full prefix"); + ret = netblock_match(&ref4, &ref4, 33); + ok(ret == true, "match: ipv4 - identity, prefix overflow"); + + ret = netblock_match(&ref6, &ref6, 127); + ok(ret == true, "match: ipv6 - identity, subnet"); + ret = netblock_match(&ref6, &ref6, 128); + ok(ret == true, "match: ipv6 - identity, full prefix"); + ret = netblock_match(&ref6, &ref6, 129); + ok(ret == true, "match: ipv6 - identity, prefix overflow"); + + // 124 dec ~ 01111100 bin + assert(sockaddr_set(&t, AF_INET, "124.0.0.0", 0) == KNOT_EOK); + ret = netblock_match(&t, &ref4, 5); + ok(ret == true, "match: ipv4 - first byte, shorter prefix"); + ret = netblock_match(&t, &ref4, 6); + ok(ret == true, "match: ipv4 - first byte, precise prefix"); + ret = netblock_match(&t, &ref4, 7); + ok(ret == false, "match: ipv4 - first byte, not match"); + + assert(sockaddr_set(&t, AF_INET, "127.170.170.124", 0) == KNOT_EOK); + ret = netblock_match(&t, &ref4, 29); + ok(ret == true, "match: ipv4 - last byte, shorter prefix"); + ret = netblock_match(&t, &ref4, 30); + ok(ret == true, "match: ipv4 - last byte, precise prefix"); + ret = netblock_match(&t, &ref4, 31); + ok(ret == false, "match: ipv4 - last byte, not match"); + + // 7C hex ~ 01111100 bin + assert(sockaddr_set(&t, AF_INET6, "7CAA::", 0) == KNOT_EOK); + ret = netblock_match(&t, &ref6, 5); + ok(ret == true, "match: ipv6 - first byte, shorter prefix"); + ret = netblock_match(&t, &ref6, 6); + ok(ret == true, "match: ipv6 - first byte, precise prefix"); + ret = netblock_match(&t, &ref6, 7); + ok(ret == false, "match: ipv6 - first byte, not match"); + + assert(sockaddr_set(&t, AF_INET6, "7FAA::AA7C", 0) == KNOT_EOK); + ret = netblock_match(&t, &ref6, 125); + ok(ret == true, "match: ipv6 - last byte, shorter prefix"); + ret = netblock_match(&t, &ref6, 126); + ok(ret == true, "match: ipv6 - last byte, precise prefix"); + ret = netblock_match(&t, &ref6, 127); + ok(ret == false, "match: ipv6 - last byte, not match"); + return 0; } diff --git a/tests/fake_server.h b/tests/fake_server.h index 4e62fc2ad53e1d87165ffb1da633e4ab609afd88..b12b3e456d55c3205fe97cd5b551f819439116fe 100644 --- a/tests/fake_server.h +++ b/tests/fake_server.h @@ -14,7 +14,7 @@ static inline void create_root_zone(server_t *server, mm_ctx_t *mm) /* SOA RDATA. */ #define SOA_RDLEN 30 static const uint8_t SOA_RDATA[SOA_RDLEN] = { - 0x02, 0x6e, 0x73, 0x00, /* ns. */ + 0x02, 'n', 's', 0x00, /* ns. */ 0x04, 'm', 'a', 'i', 'l', 0x00,/* mail. */ 0x77, 0xdf, 0x1e, 0x63, /* serial */ 0x00, 0x01, 0x51, 0x80, /* refresh */ @@ -24,11 +24,7 @@ static inline void create_root_zone(server_t *server, mm_ctx_t *mm) }; /* Insert root zone. */ - conf_zone_t *conf = malloc(sizeof(conf_zone_t)); - conf_init_zone(conf); - conf->name = strdup("."); - - zone_t *root = zone_new(conf); + zone_t *root = zone_new(ROOT_DNAME); root->contents = zone_contents_new(root->name); knot_rrset_t *soa = knot_rrset_new(root->name, KNOT_RRTYPE_SOA, KNOT_CLASS_IN, mm); @@ -57,9 +53,23 @@ static inline int create_fake_server(server_t *server, mm_ctx_t *mm) } /* Create configuration. */ - s_config = conf_new(strdup("rc:/noconf")); - conf()->identity = strdup("bogus.ns"); - conf()->version = strdup("0.11"); + const char *conf_str = "server:\n identity: bogus.ns\n version: 0.11\n"; + conf_t *conf; + ret = conf_new(&conf, conf_scheme, NULL); + if (ret != KNOT_EOK) { + return ret; + } + ret = conf_import(conf, conf_str, false); + if (ret != KNOT_EOK) { + conf_free(conf, false); + return ret; + } + ret = conf_post_open(conf); + if (ret != KNOT_EOK) { + conf_free(conf, false); + return ret; + } + conf_update(conf); /* Insert root zone. */ create_root_zone(server, mm); diff --git a/tests/journal.c b/tests/journal.c index ffb7429ef02ce8144b1ecf1e3932dddb3c380c32..06780dde3e1659f024159971506b8a2563c3d912 100644 --- a/tests/journal.c +++ b/tests/journal.c @@ -213,8 +213,7 @@ static void test_store_load(const char *jfilename) uint8_t *apex = (uint8_t *)"\4test"; /* Create fake zone. */ - conf_zone_t zconf = { .ixfr_db = (char *)jfilename, .ixfr_fslimit = filesize }; - zone_t z = { .name = apex, .conf = &zconf }; + zone_t z = { .name = apex }; /* Save and load changeset. */ changeset_t ch; @@ -223,7 +222,7 @@ static void test_store_load(const char *jfilename) ok(ret == KNOT_EOK, "journal: store changeset"); list_t l; init_list(&l); - ret = journal_load_changesets(&z, &l, 0, 1); + ret = journal_load_changesets(jfilename, &z, &l, 0, 1); ok(ret == KNOT_EOK && changesets_eq(TAIL(l), &ch), "journal: load changeset"); changeset_clear(&ch); changesets_free(&l); @@ -241,7 +240,7 @@ static void test_store_load(const char *jfilename) /* Load all changesets stored until now. */ serial--; - ret = journal_load_changesets(&z, &l, 0, serial); + ret = journal_load_changesets(jfilename, &z, &l, 0, serial); changesets_free(&l); ok(ret == KNOT_EOK, "journal: load changesets"); @@ -257,7 +256,7 @@ static void test_store_load(const char *jfilename) /* Load all changesets, except the first one that got evicted. */ init_list(&l); - ret = journal_load_changesets(&z, &l, 1, serial + 1); + ret = journal_load_changesets(jfilename, &z, &l, 1, serial + 1); changesets_free(&l); ok(ret == KNOT_EOK, "journal: load changesets after flush"); } diff --git a/tests/process_answer.c b/tests/process_answer.c index 7797d87219874478d6680abc6137108ebca21550..c3534704973185009ced2578ceb7b98e9e59757e 100644 --- a/tests/process_answer.c +++ b/tests/process_answer.c @@ -157,7 +157,7 @@ int main(int argc, char *argv[]) /* Cleanup. */ mp_delete(mm.ctx); server_deinit(&server); - conf_free(conf()); + conf_free(conf(), false); return 0; } diff --git a/tests/process_query.c b/tests/process_query.c index 095133e96304c774177c191bef3a44bd07def74e..8c2bda070d0ca96df6d6b2001a53266d9956f591 100644 --- a/tests/process_query.c +++ b/tests/process_query.c @@ -184,7 +184,7 @@ int main(int argc, char *argv[]) /* Cleanup. */ mp_delete((struct mempool *)mm.ctx); server_deinit(&server); - conf_free(conf()); + conf_free(conf(), false); return 0; } diff --git a/tests/requestor.c b/tests/requestor.c index ab4a2c3a08a79b0211ff3a5c1edb715cae197e07..00601c28428a1beb37cdfb6a68eb6702cfb4b0aa 100644 --- a/tests/requestor.c +++ b/tests/requestor.c @@ -64,7 +64,7 @@ static void* responder_thread(void *arg) #define CONNECTED_TESTS 4 #define TESTS_COUNT DISCONNECTED_TESTS + CONNECTED_TESTS -static struct knot_request *make_query(struct knot_requestor *requestor, conf_iface_t *remote) +static struct knot_request *make_query(struct knot_requestor *requestor, conf_remote_t *remote) { knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, requestor->mm); assert(pkt); @@ -75,7 +75,7 @@ static struct knot_request *make_query(struct knot_requestor *requestor, conf_i return knot_request_make(requestor->mm, dst, src, pkt, 0); } -static void test_disconnected(struct knot_requestor *requestor, conf_iface_t *remote) +static void test_disconnected(struct knot_requestor *requestor, conf_remote_t *remote) { /* Enqueue packet. */ int ret = knot_requestor_enqueue(requestor, make_query(requestor, remote)); @@ -87,7 +87,7 @@ static void test_disconnected(struct knot_requestor *requestor, conf_iface_t *re is_int(KNOT_ECONN, ret, "requestor: disconnected/wait"); } -static void test_connected(struct knot_requestor *requestor, conf_iface_t *remote) +static void test_connected(struct knot_requestor *requestor, conf_remote_t *remote) { /* Enqueue packet. */ int ret = knot_requestor_enqueue(requestor, make_query(requestor, remote)); @@ -121,8 +121,8 @@ int main(int argc, char *argv[]) mm_ctx_t mm; mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE); - conf_iface_t remote; - memset(&remote, 0, sizeof(conf_iface_t)); + conf_remote_t remote; + memset(&remote, 0, sizeof(conf_remote_t)); sockaddr_set(&remote.addr, AF_INET, "127.0.0.1", 0); sockaddr_set(&remote.via, AF_INET, "127.0.0.1", 0); @@ -170,7 +170,7 @@ int main(int argc, char *argv[]) /* Cleanup. */ mp_delete((struct mempool *)mm.ctx); server_deinit(&server); - conf_free(conf()); + conf_free(conf(), false); return 0; } diff --git a/tests/rrl.c b/tests/rrl.c index aa26114cb9f4e7980f4bd54b7a69a76dad21e164..46be1dedb3edf08f8d32f7b9d76a3f8ca1dfbc29 100644 --- a/tests/rrl.c +++ b/tests/rrl.c @@ -23,8 +23,8 @@ #include "knot/conf/conf.h" #include "knot/server/rrl.h" #include "knot/zone/zone.h" -#include "knot/conf/conf.h" #include "libknot/descriptor.h" +#include "libknot/internal/sockaddr.h" /* Enable time-dependent tests. */ //#define ENABLE_TIMED_TESTS @@ -141,10 +141,9 @@ int main(int argc, char *argv[]) is_int(KNOT_EOK, ret, "rrl: setlocks"); /* 4. N unlimited requests. */ - conf_zone_t *zone_conf = malloc(sizeof(conf_zone_t)); - conf_init_zone(zone_conf); - zone_conf->name = strdup("rrl."); - zone_t *zone = zone_new(zone_conf); + knot_dname_t *zone_name = knot_dname_from_str_alloc("rrl."); + zone_t *zone = zone_new(zone_name); + knot_dname_free(&zone_name, NULL); struct sockaddr_storage addr; struct sockaddr_storage addr6; diff --git a/tests/zone_timers.c b/tests/zone_timers.c index edc5c617a494049ab0e272011b4ab8233af7e188..38d83f92aba870a816adb85671d24e7fc8c42bec 100644 --- a/tests/zone_timers.c +++ b/tests/zone_timers.c @@ -46,10 +46,13 @@ int main(int argc, char *argv[]) const char *dbid = mkdtemp(dbid_buf); // Mockup zones. - conf_zone_t zone_conf = { .name = "test1." }; - zone_t *zone_1 = zone_new(&zone_conf); - zone_conf.name = "test2."; - zone_t *zone_2 = zone_new(&zone_conf); + knot_dname_t *zone_name; + zone_name = knot_dname_from_str_alloc("test1."); + zone_t *zone_1 = zone_new(zone_name); + knot_dname_free(&zone_name, NULL); + zone_name = knot_dname_from_str_alloc("test2."); + zone_t *zone_2 = zone_new(zone_name); + knot_dname_free(&zone_name, NULL); assert(zone_1 && zone_2); // Mockup zonedb. @@ -115,8 +118,6 @@ int main(int argc, char *argv[]) memcmp(timers, empty_timers, sizeof(timers)) == 0, "zone timers: sweep"); // Clean up. - zone_1->conf = NULL; - zone_2->conf = NULL; zone_free(&zone_1); zone_free(&zone_2); close_timers_db(db); diff --git a/tests/zonedb.c b/tests/zonedb.c index 323952d35563bea7c9d0f7a753e5caddd59dee9c..f522ba478cb1acc40a2f859513a7c76d9aa8b824 100644 --- a/tests/zonedb.c +++ b/tests/zonedb.c @@ -50,11 +50,10 @@ int main(int argc, char *argv[]) /* Populate. */ for (unsigned i = 0; i < ZONE_COUNT; ++i) { - conf_zone_t *zone_conf = malloc(sizeof(conf_zone_t)); - conf_init_zone(zone_conf); - zone_conf->name = strdup(zone_list[i]); + knot_dname_t *zone_name = knot_dname_from_str_alloc(zone_list[i]); + zones[i] = zone_new(zone_name); + knot_dname_free(&zone_name, NULL); - zones[i] = zone_new(zone_conf); if (zones[i] == NULL) { goto cleanup; }