diff options
Diffstat (limited to 'modules/nsid')
-rw-r--r-- | modules/nsid/README.rst | 35 | ||||
-rw-r--r-- | modules/nsid/nsid.c | 122 | ||||
-rw-r--r-- | modules/nsid/nsid.mk | 6 | ||||
-rw-r--r-- | modules/nsid/nsid.test.lua | 21 |
4 files changed, 184 insertions, 0 deletions
diff --git a/modules/nsid/README.rst b/modules/nsid/README.rst new file mode 100644 index 0000000..ed052c9 --- /dev/null +++ b/modules/nsid/README.rst @@ -0,0 +1,35 @@ +.. _mod-nsid: + +Name Server Identifier (NSID) +----------------------------- + +This module provides server-side support for :rfc:`5001` +and is not enabled by default. + +DNS clients can request resolver to send back its NSID along with the reply +to a DNS request. This is useful for identification of resolver instances +in larger services (using anycast or load balancers). + + +This is useful tool for debugging larger services, +as it reveals which particular resolver instance sent the reply. + +NSID value can be configured in the resolver's configuration file: + +.. code-block:: lua + + modules.load('nsid') + nsid.name('instance 1') + +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/nsid.c b/modules/nsid/nsid.c new file mode 100644 index 0000000..fef6ecd --- /dev/null +++ b/modules/nsid/nsid.c @@ -0,0 +1,122 @@ +/* Copyright (C) Knot Resolver contributors. Licensed under GNU GPLv3 or + * (at your option) any later version. See COPYING for text of the license. + * + * 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); + /* 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_verbose("[%05u. ][nsid] 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 (req->answer->opt_rr == NULL) + 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_verbose("[%05u. ][nsid] unable to add NSID option\n", req->uid); + knot_rrset_clear(req->answer->opt_rr, &req->pool); + } + + return ctx->state; +} + +KR_EXPORT +const kr_layer_api_t *nsid_layer(struct kr_module *module) +{ + static kr_layer_api_t _layer = { + .answer_finalize = &nsid_finalize, + }; + _layer.data = module; + return &_layer; +} + +KR_EXPORT +int nsid_init(struct kr_module *module) { + struct nsid_config *config = calloc(1, sizeof(struct nsid_config)); + if (config == NULL) + return kr_error(ENOMEM); + + module->data = config; + return kr_ok(); +} + +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 +struct kr_prop *nsid_props(void) +{ + static struct kr_prop prop_list[] = { + { &nsid_name, "name", "Get or set local NSID value" }, + { NULL, NULL, NULL } + }; + return prop_list; +} + +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.mk b/modules/nsid/nsid.mk new file mode 100644 index 0000000..c7d2f51 --- /dev/null +++ b/modules/nsid/nsid.mk @@ -0,0 +1,6 @@ +nsid_CFLAGS := -fPIC +nsid_LDFLAGS := -Wl,-undefined -Wl,dynamic_lookup +nsid_SOURCES := modules/nsid/nsid.c +nsid_DEPEND := $(libkres) +nsid_LIBS := $(contrib_TARGET) $(libkres_TARGET) $(libkres_LIBS) +$(call make_c_module,nsid) diff --git a/modules/nsid/nsid.test.lua b/modules/nsid/nsid.test.lua new file mode 100644 index 0000000..ce178f8 --- /dev/null +++ b/modules/nsid/nsid.test.lua @@ -0,0 +1,21 @@ +-- disable networking so we can get SERVFAIL immediatelly +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, +} |