Commit 70411173 authored by Petr Špaček's avatar Petr Špaček Committed by Vladimír Čunát

modules/nsid: new module for +NSID (server-side)

Minor changes to be blamed on Vladimir.
parent 65d1c322
Pipeline #42212 canceled with stages
in 3 minutes and 6 seconds
......@@ -4,6 +4,7 @@ Knot Resolver 3.x.y (201y-mm-dd)
New features
------------
- module edns_keepalive to implement server side of RFC 7828 (#408)
- module nsid to implement server side of RFC 5001 (#289)
Module API changes
------------------
......
......@@ -22,6 +22,7 @@ Knot Resolver modules
.. include:: ../modules/renumber/README.rst
.. .. include:: ../modules/cookies/README.rst
.. include:: ../modules/bogus_log/README.rst
.. include:: ../modules/nsid/README.rst
.. include:: ../modules/workarounds/README.rst
.. include:: ../modules/dnstap/README.rst
.. include:: ../modules/ta_signal_query/README.rst
......
......@@ -14,6 +14,7 @@ endif
# List of Lua modules
ifeq ($(HAS_lua),yes)
modules_TARGETS += bogus_log \
nsid \
etcd \
ta_sentinel \
graphite \
......
.. _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')
/* 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);
nsid_CFLAGS := -fPIC
nsid_SOURCES := modules/nsid/nsid.c
nsid_DEPEND := $(libkres)
nsid_LIBS := $(contrib_TARGET) $(libkres_TARGET) $(libkres_LIBS)
$(call make_c_module,nsid)
-- 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,
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment