Commit 6cf1437f authored by Daniel Salzman's avatar Daniel Salzman

modules: integrate new configuration

parent b533bb40
......@@ -80,6 +80,7 @@
/src/khost
/src/knsupdate
/src/knsec3hash
/src/rosedb_tool
# Code coverage
*.gcda
......
#
# This is a sample of a minimal configuration file for Knot DNS when used with RoseDB.
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;
}
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";
}
zones {
query_module {
rosedb "/etc/knot/rosedb";
# catchall dnsproxy
# dnsproxy "192.0.2.1";
}
# dnsproxy for specific domain name
#
# example.com {
# query_module {
# dnsproxy "198.51.100.1";
# }
# }
}
log {
syslog {
# Log info and more serious events to syslog
any info;
}
# Log warnings, errors and criticals to stderr
stderr {
any warning;
}
}
......@@ -348,6 +348,7 @@ if HAVE_ROSEDB
libknotd_la_SOURCES += \
knot/modules/rosedb.c \
knot/modules/rosedb.h
libknotd_la_LIBADD += libknot-yparser.la
endif # HAVE_ROSEDB
# Create storage and run-time directories
......
......@@ -31,6 +31,8 @@
#include "knot/conf/confdb.h"
#include "knot/conf/tools.h"
#include "knot/common/log.h"
#include "knot/nameserver/query_module.h"
#include "knot/nameserver/internet.h"
#include "knot/server/dthreads.h"
#include "libknot/errcode.h"
#include "libknot/internal/macros.h"
......@@ -181,6 +183,13 @@ int conf_post_open(
conf->hostname = sockaddr_hostname();
int ret = conf_activate_modules(conf, NULL, &conf->query_modules,
&conf->query_plan);
if (ret != KNOT_EOK) {
free(conf->hostname);
return ret;
}
return KNOT_EOK;
}
......@@ -213,6 +222,11 @@ void conf_free(
conf->api->txn_abort(&conf->read_txn);
free(conf->hostname);
if (conf->query_plan != NULL) {
conf_deactivate_modules(conf, &conf->query_modules,
conf->query_plan);
}
if (!is_clone) {
conf->api->deinit(conf->db);
free(conf->mm);
......@@ -255,6 +269,94 @@ void conf_free(
free(conf);
}
int conf_activate_modules(
conf_t *conf,
knot_dname_t *zone_name,
list_t *query_modules,
struct query_plan **query_plan)
{
if (conf == NULL || query_modules == NULL || query_plan == NULL) {
return KNOT_EINVAL;
}
conf_val_t val;
// Get list of associated modules.
if (zone_name != NULL) {
val = conf_zone_get(conf, C_MODULE, zone_name);
} else {
val = conf_default_get(conf, C_MODULE);
}
if (val.code == KNOT_ENOENT) {
return KNOT_EOK;
} else if (val.code != KNOT_EOK) {
return val.code;
}
// Create query plan.
*query_plan = query_plan_create(conf->mm);
if (*query_plan == NULL) {
return KNOT_ENOMEM;
}
if (zone_name != NULL) {
// Only supported zone class is now IN.
internet_query_plan(*query_plan);
}
// Initialize query modules list.
init_list(query_modules);
// Open the modules.
while (val.code == KNOT_EOK) {
conf_mod_id_t *mod_id = conf_mod_id(&val);
if (mod_id == NULL) {
return KNOT_ENOMEM;
}
// Open the module.
struct query_module *mod = query_module_open(conf, mod_id, conf->mm);
if (mod == NULL) {
conf_free_mod_id(mod_id);
return KNOT_ENOMEM;
}
// Load the module.
int ret = mod->load(*query_plan, mod);
if (ret != KNOT_EOK) {
query_module_close(mod);
return ret;
}
add_tail(query_modules, &mod->node);
conf_val_next(&val);
}
return KNOT_EOK;
}
void conf_deactivate_modules(
conf_t *conf,
list_t *query_modules,
struct query_plan *query_plan)
{
if (conf == NULL || query_modules == NULL || query_plan == NULL) {
return;
}
// Free query modules list.
struct query_module *mod = NULL, *next = NULL;
WALK_LIST_DELSAFE(mod, next, *query_modules) {
mod->unload(mod);
query_module_close(mod);
}
// Free query plan.
query_plan_free(query_plan);
}
static int parser_process(
conf_t *conf,
namedb_txn_t *txn,
......@@ -628,6 +730,21 @@ conf_val_t conf_id_get(
(id == NULL) ? 0 : id->len);
}
conf_val_t conf_mod_get(
conf_t *conf,
const yp_name_t *key1_name,
const conf_mod_id_t *mod_id)
{
// Check for empty input.
if (key1_name == NULL || mod_id == NULL) {
conf_val_t val = { NULL };
val.code = KNOT_EINVAL;
return val;
}
return raw_id_get(conf, mod_id->name, key1_name, mod_id->data, mod_id->len);
}
conf_val_t conf_zone_get(
conf_t *conf,
const yp_name_t *key1_name,
......@@ -927,6 +1044,54 @@ const knot_dname_t* conf_dname(
}
}
conf_mod_id_t* conf_mod_id(
conf_val_t *val)
{
assert(val != NULL);
assert(val->item->type == YP_TDATA);
conf_mod_id_t *mod_id = NULL;
if (val->code == KNOT_EOK) {
conf_db_val(val);
// Make copy of mod_id because pointers are not persisent in db.
mod_id = malloc(sizeof(conf_mod_id_t));
if (mod_id == NULL) {
return NULL;
}
// Copy module name.
size_t name_len = 1 + val->data[0];
mod_id->name = malloc(name_len + 1);
if (mod_id->name == NULL) {
free(mod_id);
return NULL;
}
memcpy(mod_id->name, val->data, name_len);
// Copy module identifier.
mod_id->len = val->len - name_len;
mod_id->data = malloc(mod_id->len);
if (mod_id->data == NULL) {
free(mod_id->name);
free(mod_id);
return NULL;
}
memcpy(mod_id->data, val->data + name_len, mod_id->len);
}
return mod_id;
}
void conf_free_mod_id(
conf_mod_id_t *mod_id)
{
free(mod_id->name);
free(mod_id->data);
free(mod_id);
}
struct sockaddr_storage conf_addr(
conf_val_t *val,
const char *sock_base_dir)
......
......@@ -52,6 +52,10 @@ typedef struct {
char *filename;
// Temporary database path.
char *tmp_dir;
// List of active query modules.
list_t query_modules;
// Default query modules plan.
struct query_plan *query_plan;
} conf_t;
typedef struct {
......@@ -78,6 +82,12 @@ typedef struct {
int code; // Return code.
} conf_iter_t;
typedef struct {
yp_name_t *name;
uint8_t *data;
size_t len;
} conf_mod_id_t;
extern conf_t *s_conf;
static inline conf_t* conf(void) {
......@@ -107,6 +117,19 @@ void conf_free(
bool is_clone
);
int conf_activate_modules(
conf_t *conf,
knot_dname_t *zone_name,
list_t *query_modules,
struct query_plan **query_plan
);
void conf_deactivate_modules(
conf_t *conf,
list_t *query_modules,
struct query_plan *query_plan
);
int conf_parse(
conf_t *conf,
namedb_txn_t *txn,
......@@ -142,6 +165,12 @@ conf_val_t conf_id_get(
conf_val_t *id
);
conf_val_t conf_mod_get(
conf_t *conf,
const yp_name_t *key1_name,
const conf_mod_id_t *mod_id
);
conf_val_t conf_zone_get(
conf_t *conf,
const yp_name_t *key1_name,
......@@ -211,6 +240,14 @@ const knot_dname_t* conf_dname(
conf_val_t *val
);
conf_mod_id_t* conf_mod_id(
conf_val_t *val
);
void conf_free_mod_id(
conf_mod_id_t *mod_id
);
struct sockaddr_storage conf_addr(
conf_val_t *val,
const char *sock_base_dir
......
......@@ -28,6 +28,15 @@
#include "libknot/rrtype/opt.h"
#include "dnssec/lib/dnssec/tsig.h"
#include "knot/modules/synth_record.h"
#include "knot/modules/dnsproxy.h"
#ifdef HAVE_ROSEDB
#include "knot/modules/rosedb.h"
#endif
#if USE_DNSTAP
#include "knot/modules/dnstap.h"
#endif
static const lookup_table_t key_algs[] = {
{ DNSSEC_TSIG_HMAC_MD5, "hmac-md5" },
{ DNSSEC_TSIG_HMAC_SHA1, "hmac-sha1" },
......@@ -139,6 +148,8 @@ static const yp_item_t desc_remote[] = {
{ C_DNSSEC_KEYDIR, YP_TSTR, YP_VSTR = { "keys" } }, \
{ C_SIG_LIFETIME, YP_TINT, YP_VINT = { 3 * 3600, INT32_MAX, 30 * 24 * 3600, YP_STIME } }, \
{ C_SERIAL_POLICY, YP_TOPT, YP_VOPT = { serial_policies, SERIAL_POLICY_INCREMENT } }, \
{ C_MODULE, YP_TDATA, YP_VDATA = { 0, NULL, mod_id_to_bin, mod_id_to_txt }, \
YP_FMULTI, { check_modref } }, \
{ C_COMMENT, YP_TSTR, YP_VNONE },
static const yp_item_t desc_template[] = {
......@@ -170,6 +181,16 @@ const yp_item_t conf_scheme[] = {
{ C_ACL, YP_TGRP, YP_VGRP = { desc_acl }, YP_FMULTI },
{ C_CTL, YP_TGRP, YP_VGRP = { desc_control } },
{ C_RMT, YP_TGRP, YP_VGRP = { desc_remote }, YP_FMULTI },
/* MODULES */
{ C_MOD_SYNTH_RECORD, YP_TGRP, YP_VGRP = { scheme_mod_synth_record }, YP_FMULTI },
{ C_MOD_DNSPROXY, YP_TGRP, YP_VGRP = { scheme_mod_dnsproxy }, YP_FMULTI },
#if HAVE_ROSEDB
{ C_MOD_ROSEDB, YP_TGRP, YP_VGRP = { scheme_mod_rosedb }, YP_FMULTI },
#endif
#if USE_DNSTAP
{ C_MOD_DNSTAP, YP_TGRP, YP_VGRP = { scheme_mod_dnstap }, YP_FMULTI },
#endif
/***********/
{ C_TPL, YP_TGRP, YP_VGRP = { desc_template }, YP_FMULTI },
{ C_ZONE, YP_TGRP, YP_VGRP = { desc_zone }, YP_FMULTI },
{ C_LOG, YP_TGRP, YP_VGRP = { desc_log }, YP_FMULTI },
......
......@@ -129,6 +129,56 @@ int hex_text_to_txt(
return KNOT_EOK;
}
int mod_id_to_bin(
char const *txt,
size_t txt_len,
uint8_t *bin,
size_t *bin_len)
{
// Check for "mod_name/mod_id" format.
char *pos = index(txt, '/');
if (pos == NULL) {
return KNOT_EINVAL;
}
uint8_t name_len = pos - txt;
char *id = pos + 1;
size_t id_len = txt_len - name_len - 1;
// Output is mod_name in yp_name_t format and zero terminated id string.
size_t total_out_len = 1 + name_len + id_len + 1;
// Check for enough output room.
if (*bin_len < total_out_len) {
return KNOT_ESPACE;
}
// Write mod_name in yp_name_t format.
bin[0] = name_len;
memcpy(bin + 1, txt, name_len);
// Write mod_id as zero terminated string.
memcpy(bin + 1 + name_len, id, id_len + 1);
// Set output length.
*bin_len = total_out_len;
return KNOT_EOK;
}
int mod_id_to_txt(
uint8_t const *bin,
size_t bin_len,
char *txt,
size_t *txt_len)
{
int ret = snprintf(txt, *txt_len, "%.*s/%s", (int)bin[0], bin + 1,
bin + 1 + bin[0]);
if (ret <= 0 || ret >= *txt_len) {
return KNOT_ESPACE;
}
*txt_len = ret;
return KNOT_EOK;
}
int check_ref(
conf_args_t *args)
{
......@@ -139,6 +189,18 @@ int check_ref(
args->data, args->data_len, NULL);
}
int check_modref(
conf_args_t *args)
{
const yp_name_t *mod_name = (const yp_name_t *)args->data;
const uint8_t *id = args->data + 1 + args->data[0];
size_t id_len = args->data_len - 1 - args->data[0];
// Try to find the module with id.
return conf_db_get(args->conf, args->txn, mod_name, NULL, id, id_len,
NULL);
}
int include_file(
conf_args_t *args)
{
......
......@@ -60,10 +60,28 @@ int hex_text_to_txt(
size_t *txt_len
);
int mod_id_to_bin(
char const *txt,
size_t txt_len,
uint8_t *bin,
size_t *bin_len
);
int mod_id_to_txt(
uint8_t const *bin,
size_t bin_len,
char *txt,
size_t *txt_len
);
int check_ref(
conf_args_t *args
);
int check_modref(
conf_args_t *args
);
int include_file(
conf_args_t *args
);
......@@ -15,14 +15,26 @@
*/
#include "libknot/processing/requestor.h"
#include "knot/common/log.h"
#include "knot/modules/dnsproxy.h"
#include "knot/nameserver/capture.h"
#include "knot/nameserver/process_query.h"
/* Module configuration scheme. */
#define MOD_REMOTE "\x06""remote"
const yp_item_t scheme_mod_dnsproxy[] = {
{ C_ID, YP_TSTR, YP_VNONE },
{ MOD_REMOTE, YP_TADDR, YP_VADDR = { 53 } },
{ C_COMMENT, YP_TSTR, YP_VNONE },
{ NULL }
};
/* Defines. */
#define MODULE_ERR(msg...) log_error("module 'dnsproxy', " msg)
struct dnsproxy {
conf_iface_t remote;
struct sockaddr_storage remote;
};
static int dnsproxy_fwd(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx)
......@@ -49,8 +61,9 @@ static int dnsproxy_fwd(int state, knot_pkt_t *pkt, struct query_data *qdata, vo
}
bool is_tcp = net_is_connected(qdata->param->socket);
struct knot_request *req = knot_request_make(re.mm, (struct sockaddr *)&proxy->remote.addr,
NULL, qdata->query, is_tcp ? 0 : KNOT_RQ_UDP);
struct knot_request *req;
req = knot_request_make(re.mm, (const struct sockaddr *)&proxy->remote,
NULL, qdata->query, is_tcp ? 0 : KNOT_RQ_UDP);
if (req == NULL) {
return state; /* Ignore, not enough memory. */
}
......@@ -58,7 +71,8 @@ static int dnsproxy_fwd(int state, knot_pkt_t *pkt, struct query_data *qdata, vo
/* Forward request. */
ret = knot_requestor_enqueue(&re, req);
if (ret == KNOT_EOK) {
struct timeval tv = { conf()->max_conn_hs, 0 };
conf_val_t val = conf_get(conf(), C_SRV, C_MAX_CONN_HANDSHAKE);
struct timeval tv = { conf_int(&val), 0 };
ret = knot_requestor_exec(&re, &tv);
} else {
knot_request_free(re.mm, req);
......@@ -77,6 +91,10 @@ static int dnsproxy_fwd(int state, knot_pkt_t *pkt, struct query_data *qdata, vo
int dnsproxy_load(struct query_plan *plan, struct query_module *self)
{
if (plan == NULL || self == NULL) {
return KNOT_EINVAL;
}
struct dnsproxy *proxy = mm_alloc(self->mm, sizeof(struct dnsproxy));
if (proxy == NULL) {
MODULE_ERR("not enough memory");
......@@ -84,24 +102,28 @@ int dnsproxy_load(struct query_plan *plan, struct query_module *self)
}
memset(proxy, 0, sizeof(struct dnsproxy));
/* Determine IPv4/IPv6 */
int family = AF_INET;
if (strchr(self->param, ':')) {
family = AF_INET6;
}
int ret = sockaddr_set(&proxy->remote.addr, family, self->param, 53);
if (ret != KNOT_EOK) {
MODULE_ERR("invalid proxy address: '%s'", self->param);
conf_val_t val = conf_mod_get(self->config, MOD_REMOTE, self->id);
if (val.code != KNOT_EOK) {
if (val.code == KNOT_EINVAL) {
MODULE_ERR("no remote proxy address for '%s'",
self->id->data);
}
mm_free(self->mm, proxy);
return KNOT_EINVAL;
return val.code;
}
proxy->remote = conf_addr(&val, NULL);
self->ctx = proxy;
return query_plan_step(plan, QPLAN_BEGIN, dnsproxy_fwd, proxy);
return query_plan_step(plan, QPLAN_BEGIN, dnsproxy_fwd, self->ctx);
}
int dnsproxy_unload(struct query_module *self)
{
if (self == NULL) {
return KNOT_EINVAL;
}
mm_free(self->mm, self->ctx);
return KNOT_EOK;
}
......@@ -35,6 +35,10 @@
#include "knot/nameserver/query_module.h"
/*! \brief Module scheme. */
#define C_MOD_DNSPROXY "\x0C""mod-dnsproxy"
extern const yp_item_t scheme_mod_dnsproxy[];
/*! \brief Module interface. */
int dnsproxy_load(struct query_plan *plan, struct query_module *self);
int dnsproxy_unload(struct query_module *self);
......
......@@ -16,15 +16,24 @@
#include <sys/stat.h>
#include "knot/common/log.h"
#include "knot/modules/dnstap.h"
#include "knot/nameserver/query_module.h"
#include "knot/nameserver/process_query.h"
#include "dnstap/dnstap.pb-c.h"
#include "dnstap/writer.h"
#include "dnstap/message.h"
#include "dnstap/dnstap.h"
#include "libknot/libknot.h"
#include "libknot/descriptor.h"
/* Module configuration scheme. */
#define MOD_SINK "\x04""sink"
const yp_item_t scheme_mod_dnstap[] = {
{ C_ID, YP_TSTR, YP_VNONE },
{ MOD_SINK, YP_TSTR, YP_VNONE },
{ C_COMMENT, YP_TSTR, YP_VNONE },
{ NULL }
};
/* Defines. */
#define MODULE_ERR(msg...) log_error("module 'dnstap', " msg)
......@@ -171,9 +180,21 @@ static struct fstrm_writer* dnstap_writer(const char *path)
int dnstap_load(struct query_plan *plan, struct query_module *self)
{
if (plan == NULL || self == NULL) {
return KNOT_EINVAL;
}
conf_val_t val = conf_mod_get(self->config, MOD_SINK, self->id);
if (val.code != KNOT_EOK) {
if (val.code == KNOT_EINVAL) {
MODULE_ERR("no sink for '%s'", self->id->data);
}
return val.code;
}
const char *sink = conf_str(&val);
/* Initialize the writer and the options. */
int ret = KNOT_ENOMEM;
struct fstrm_writer *writer = dnstap_writer(self->param);
struct fstrm_writer *writer = dnstap_writer(sink);
if (writer == NULL) {
goto fail;
}
......@@ -185,7 +206,8 @@ int dnstap_load(struct query_plan *plan, struct query_module *self)
}
/* Initialize queues. */
size_t qcount = conf_udp_threads(self->config) + conf_tcp_threads(self->config);
size_t qcount = conf_udp_threads(self->config) +
conf_tcp_threads(self->config);
fstrm_iothr_options_set_num_input_queues(opt, qcount);
/* Create the I/O thread. */
......@@ -196,6 +218,7 @@ int dnstap_load(struct query_plan *plan, struct query_module *self)
fstrm_writer_destroy(&writer);
goto fail;
}
self->ctx = iothread;
/* Hook to the query plan. */
......@@ -205,12 +228,16 @@ int dnstap_load(struct query_plan *plan, struct query_module *self)
return KNOT_EOK;
fail:
MODULE_ERR("init failed, params '%s' (%s)", self->param, knot_strerror(ret));
return ret;
MODULE_ERR("failed for init sink '%s'", sink);
return KNOT_ENOMEM;
}
int dnstap_unload(struct query_module *self)
{
if (self == NULL) {
return KNOT_EINVAL;
}
struct fstrm_iothr* iothread = self->ctx;
fstrm_iothr_destroy(&iothread);
return KNOT_EOK;
......
......@@ -26,6 +26,10 @@
#include "knot/nameserver/query_module.h"
/*! \brief Module scheme. */
#define C_MOD_DNSTAP "\x0A""mod-dnstap"
extern const yp_item_t scheme_mod_dnstap[];
/*! \brief Module interface. */
int dnstap_load(struct query_plan *plan, struct query_module *self);
int dnstap_unload(struct query_module *self);
......
......@@ -16,13 +16,23 @@
#include <lmdb.h>
#include "dnssec/random.h"
#include "knot/common/log.h"
#include "knot/modules/rosedb.h"
#include "knot/nameserver/process_query.h"
#include "libknot/rrtype/rdname.h"
#include "dnssec/random.h"
#include "libknot/rrset-dump.h"
#include "libknot/libknot.h"
#include "libknot/internal/utils.h"
/* Module configuration scheme. */
#define MOD_DBDIR "\x06""dbdir"
const yp_item_t scheme_mod_rosedb[] = {
{ C_ID, YP_TSTR, YP_VNONE },
{ MOD_DBDIR, YP_TSTR, YP_VNONE },
{ C_COMMENT, YP_TSTR, YP_VNONE },
{ NULL }
};
/*! \note Below is an implementation of basic RR cache in LMDB,
* it shall be replaced with the namedb API later, when
* it supports multiple dbs + the basic "node" representation,
......@@ -447,7 +457,12 @@ static int rosedb_send_log(int sock, struct sockaddr *dst_addr, knot_pkt_t *pkt,
STREAM_WRITE(stream, &maxlen, strftime, "%b %d %H:%M:%S ", &tm);
/* Host name / Component. */
STREAM_WRITE(stream, &maxlen, snprintf, "%s ", conf()->identity);
conf_val_t val = conf_get(conf(), C_SRV, C_IDENT);
if (val.code != KNOT_EOK || val.len <= 1) {
STREAM_WRITE(stream, &maxlen, snprintf, "%s ", conf()->hostname);
} else {
STREAM_WRITE(stream, &maxlen, snprintf, "%s ", conf_str(&val));
}
STREAM_WRITE(stream, &maxlen, snprintf, "%s[%lu]: ", PACKAGE_NAME, (unsigned long) getpid());