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
......@@ -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')'instance 1')
You can also obtain configured NSID value:
.. code-block:: lua
instance 1
The module can be disabled at run-time:
.. code-block:: lua
/* 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
* 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;
const kr_layer_api_t *nsid_layer(struct kr_module *module)
static kr_layer_api_t _layer = {
.answer_finalize = &nsid_finalize,
}; = module;
return &_layer;
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");
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);
return NULL;
struct kr_prop *nsid_props(void)
static struct kr_prop prop_list[] = {
{ &nsid_name, "name", "Get or set local NSID value" },
return prop_list;
int nsid_deinit(struct kr_module *module) {
struct nsid_config *config = module->data;
if (config != NULL) {
module->data = NULL;
return kr_ok();
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 interface
local function test_nsid_name()
if nsid then
same(, nil, 'NSID modes not provide default NSID value')
same('123456'), '123456', 'NSID value can be changed')
same(, '123456', 'NSID module remembers configured NSID value')
same(, nil, 'NSID module reload removes configured value')
return {
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment