diff options
Diffstat (limited to 'modules/nsid')
-rw-r--r-- | modules/nsid/.packaging/test.config | 4 | ||||
-rw-r--r-- | modules/nsid/README.rst | 35 | ||||
-rw-r--r-- | modules/nsid/meson.build | 25 | ||||
-rw-r--r-- | modules/nsid/nsid.c | 114 | ||||
-rw-r--r-- | modules/nsid/nsid.test.lua | 22 |
5 files changed, 200 insertions, 0 deletions
diff --git a/modules/nsid/.packaging/test.config b/modules/nsid/.packaging/test.config new file mode 100644 index 0000000..de54cce --- /dev/null +++ b/modules/nsid/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('nsid') +assert(nsid) +quit() diff --git a/modules/nsid/README.rst b/modules/nsid/README.rst new file mode 100644 index 0000000..3d0bf4e --- /dev/null +++ b/modules/nsid/README.rst @@ -0,0 +1,35 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-nsid: + +Name Server Identifier (NSID) +============================= + +Module ``nsid`` provides server-side support for :rfc:`5001` +which allows DNS clients to request resolver to send back its NSID +along with the reply to a DNS request. +This is useful for debugging larger resolver farms +(e.g. when using :ref:`systemd-multiple-instances`, anycast or load balancers). + +NSID value can be configured in the resolver's configuration file: + +.. code-block:: lua + + modules.load('nsid') + nsid.name('instance 1') + +.. tip:: When dealing with Knot Resolver running in `multiple instances` + managed with systemd see :ref:`instance-specific-configuration`. + +You can also obtain configured NSID value: + +.. code-block:: lua + + > nsid.name() + 'instance 1' + +The module can be disabled at run-time: + +.. code-block:: lua + + modules.unload('nsid') diff --git a/modules/nsid/meson.build b/modules/nsid/meson.build new file mode 100644 index 0000000..354e70b --- /dev/null +++ b/modules/nsid/meson.build @@ -0,0 +1,25 @@ +# C module: nsid +# SPDX-License-Identifier: GPL-3.0-or-later + +nsid_src = files([ + 'nsid.c', +]) +c_src_lint += nsid_src + +nsid_mod = shared_module( + 'nsid', + nsid_src, + dependencies: [ + libknot, + luajit, + ], + include_directories: mod_inc_dir, + name_prefix: '', + install: true, + install_dir: modules_dir, + link_with: kresd, +) + +config_tests += [ + ['nsid', files('nsid.test.lua')], +] diff --git a/modules/nsid/nsid.c b/modules/nsid/nsid.c new file mode 100644 index 0000000..a67dcdb --- /dev/null +++ b/modules/nsid/nsid.c @@ -0,0 +1,114 @@ +/* Copyright (C) Knot Resolver contributors + * SPDX-License-Identifier: GPL-3.0-or-later + * + * This module provides NSID support according to RFC 5001. */ + +#include <libknot/packet/pkt.h> +#include <contrib/cleanup.h> +#include <ccan/json/json.h> +#include <lauxlib.h> + +#include "daemon/engine.h" +#include "lib/layer.h" + +struct nsid_config { + uint8_t *local_nsid; + size_t local_nsid_len; +}; + +static int nsid_finalize(kr_layer_t *ctx) { + const struct kr_module *module = ctx->api->data; + const struct nsid_config *config = module->data; + struct kr_request *req = ctx->req; + + /* no local NSID configured, do nothing */ + if (config->local_nsid == NULL) + return ctx->state; + + const knot_rrset_t *src_opt = req->qsource.packet->opt_rr; + /* no EDNS in request, do nothing */ + if (src_opt == NULL) + return ctx->state; + + const uint8_t *req_nsid = knot_edns_get_option(src_opt, KNOT_EDNS_OPTION_NSID, NULL); + /* NSID option must be explicitly requested */ + if (req_nsid == NULL) + return ctx->state; + + /* Check violation of https://tools.ietf.org/html/rfc5001#section-2.1: + * The resolver MUST NOT include any NSID payload data in the query */ + if (knot_edns_opt_get_length(req_nsid) != 0) + kr_log_debug(NSID, "[%05u. ] FORMERR: NSID option in query " + "must not contain payload, continuing\n", req->uid); + /* FIXME: actually change RCODE in answer to FORMERR? */ + + /* Sanity check, answer should have EDNS as well but who knows ... */ + if (kr_fails_assert(req->answer->opt_rr)) + return ctx->state; + + if (knot_edns_add_option(req->answer->opt_rr, KNOT_EDNS_OPTION_NSID, + config->local_nsid_len, config->local_nsid, + &req->pool) != KNOT_EOK) { + /* something went wrong and there is no way to salvage content of OPT RRset */ + kr_log_debug(NSID, "[%05u. ] unable to add NSID option\n", req->uid); + knot_rrset_clear(req->answer->opt_rr, &req->pool); + } + + return ctx->state; +} + +static char* nsid_name(void *env, struct kr_module *module, const char *args) +{ + struct engine *engine = env; + struct nsid_config *config = module->data; + if (args) { /* set */ + /* API is not binary safe, we need to fix this one day */ + uint8_t *arg_copy = (uint8_t *)strdup(args); + if (arg_copy == NULL) + luaL_error(engine->L, "[nsid] error while allocating new NSID value\n"); + free(config->local_nsid); + config->local_nsid = arg_copy; + config->local_nsid_len = strlen(args); + } + + /* get */ + if (config->local_nsid != NULL) + return json_encode_string((char *)config->local_nsid); + else + return NULL; +} + +KR_EXPORT +int nsid_init(struct kr_module *module) { + static kr_layer_api_t layer = { + .answer_finalize = &nsid_finalize, + }; + layer.data = module; + module->layer = &layer; + + static const struct kr_prop props[] = { + { &nsid_name, "name", "Get or set local NSID value" }, + { NULL, NULL, NULL } + }; + module->props = props; + + struct nsid_config *config = calloc(1, sizeof(struct nsid_config)); + if (config == NULL) + return kr_error(ENOMEM); + + module->data = config; + return kr_ok(); +} + +KR_EXPORT +int nsid_deinit(struct kr_module *module) { + struct nsid_config *config = module->data; + if (config != NULL) { + free(config->local_nsid); + free(config); + module->data = NULL; + } + return kr_ok(); +} + +KR_MODULE_EXPORT(nsid) diff --git a/modules/nsid/nsid.test.lua b/modules/nsid/nsid.test.lua new file mode 100644 index 0000000..8e5d4f9 --- /dev/null +++ b/modules/nsid/nsid.test.lua @@ -0,0 +1,22 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- disable networking so we can get SERVFAIL immediately +net.ipv4 = false +net.ipv6 = false + +-- test for nsid.name() interface +local function test_nsid_name() + if nsid then + modules.unload('nsid') + end + modules.load('nsid') + same(nsid.name(), nil, 'NSID modes not provide default NSID value') + same(nsid.name('123456'), '123456', 'NSID value can be changed') + same(nsid.name(), '123456', 'NSID module remembers configured NSID value') + modules.unload('nsid') + modules.load('nsid') + same(nsid.name(), nil, 'NSID module reload removes configured value') +end + +return { + test_nsid_name, +} |