From 75607a957bcaef602a5c2d5ce6a39964bfad2b5f Mon Sep 17 00:00:00 2001
From: Marek Vavrusa <marek.vavrusa@nic.cz>
Date: Wed, 29 Jan 2014 10:49:56 +0100
Subject: [PATCH] Split packet processing API from name-server.h to
 libknot/processing

---
 Knot.files                                    |   8 +-
 src/Makefile.am                               |   6 +-
 src/knot/nameserver/axfr.c                    |   2 +-
 src/knot/nameserver/internet.c                |   2 +-
 src/knot/nameserver/internet.h                |   4 +-
 src/knot/nameserver/ixfr.c                    |   2 +-
 src/knot/nameserver/name-server.c             |  96 -------------
 src/knot/nameserver/name-server.h             | 110 ---------------
 src/knot/nameserver/nsec_proofs.c             |   2 +-
 .../{ns_proc_query.c => process_query.c}      |  86 ++++++------
 .../{ns_proc_query.h => process_query.h}      |  40 +++---
 src/knot/nameserver/update.c                  |   2 +-
 src/knot/server/notify.c                      |   2 +-
 src/knot/server/tcp-handler.c                 |  24 ++--
 src/knot/server/udp-handler.c                 |  46 ++++---
 src/libknot/processing/process.c              | 119 ++++++++++++++++
 src/libknot/processing/process.h              | 127 ++++++++++++++++++
 src/libknot/tsig.h                            |  13 ++
 tests/Makefile.inc                            |   2 +-
 tests/TESTS                                   |   2 +-
 tests/{ns.c => process_query.c}               |  44 +++---
 21 files changed, 405 insertions(+), 334 deletions(-)
 rename src/knot/nameserver/{ns_proc_query.c => process_query.c} (85%)
 rename src/knot/nameserver/{ns_proc_query.h => process_query.h} (82%)
 create mode 100644 src/libknot/processing/process.c
 create mode 100644 src/libknot/processing/process.h
 rename tests/{ns.c => process_query.c} (87%)

diff --git a/Knot.files b/Knot.files
index 350c24524..f5afd3251 100644
--- a/Knot.files
+++ b/Knot.files
@@ -113,10 +113,10 @@ src/knot/nameserver/ixfr.c
 src/knot/nameserver/ixfr.h
 src/knot/nameserver/name-server.c
 src/knot/nameserver/name-server.h
-src/knot/nameserver/ns_proc_query.c
-src/knot/nameserver/ns_proc_query.h
 src/knot/nameserver/nsec_proofs.c
 src/knot/nameserver/nsec_proofs.h
+src/knot/nameserver/process_query.c
+src/knot/nameserver/process_query.h
 src/knot/nameserver/update.c
 src/knot/nameserver/update.h
 src/knot/other/debug.h
@@ -205,6 +205,8 @@ src/libknot/packet/compr.h
 src/libknot/packet/pkt.c
 src/libknot/packet/pkt.h
 src/libknot/packet/wire.h
+src/libknot/processing/process.c
+src/libknot/processing/process.h
 src/libknot/rdata.h
 src/libknot/rrset-dump.c
 src/libknot/rrset-dump.h
@@ -284,8 +286,8 @@ tests/fdset.c
 tests/hattrie.c
 tests/hhash.c
 tests/journal.c
-tests/ns.c
 tests/pkt.c
+tests/process_query.c
 tests/rrl.c
 tests/rrset.c
 tests/runtests.c
diff --git a/src/Makefile.am b/src/Makefile.am
index c3d79fd47..dceb1d471 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -113,6 +113,8 @@ libknot_la_SOURCES =				\
 	libknot/packet/compr.c			\
 	libknot/packet/compr.h			\
 	libknot/packet/wire.h			\
+	libknot/processing/process.c	\
+	libknot/processing/process.h	\
 	libknot/dname.c				\
 	libknot/consts.h			\
 	libknot/edns.h				\
@@ -240,8 +242,8 @@ libknotd_la_SOURCES =				\
 	knot/nameserver/ixfr.h			\
 	knot/nameserver/name-server.c		\
 	knot/nameserver/name-server.h		\
-	knot/nameserver/ns_proc_query.c		\
-	knot/nameserver/ns_proc_query.h		\
+	knot/nameserver/process_query.c		\
+	knot/nameserver/process_query.h		\
 	knot/nameserver/nsec_proofs.c		\
 	knot/nameserver/nsec_proofs.h		\
 	knot/nameserver/update.c		\
diff --git a/src/knot/nameserver/axfr.c b/src/knot/nameserver/axfr.c
index e494c5922..3126e18d7 100644
--- a/src/knot/nameserver/axfr.c
+++ b/src/knot/nameserver/axfr.c
@@ -18,7 +18,7 @@
 
 #include "knot/nameserver/axfr.h"
 #include "knot/nameserver/internet.h"
-#include "knot/nameserver/ns_proc_query.h"
+#include "knot/nameserver/process_query.h"
 #include "libknot/util/debug.h"
 #include "common/descriptor.h"
 #include "common/lists.h"
diff --git a/src/knot/nameserver/internet.c b/src/knot/nameserver/internet.c
index ca4366855..2c269bef4 100644
--- a/src/knot/nameserver/internet.c
+++ b/src/knot/nameserver/internet.c
@@ -2,7 +2,7 @@
 
 #include "knot/nameserver/internet.h"
 #include "knot/nameserver/nsec_proofs.h"
-#include "knot/nameserver/ns_proc_query.h"
+#include "knot/nameserver/process_query.h"
 #include "libknot/common.h"
 #include "libknot/rdata.h"
 #include "libknot/util/debug.h"
diff --git a/src/knot/nameserver/internet.h b/src/knot/nameserver/internet.h
index dc114f1dc..e419bec8f 100644
--- a/src/knot/nameserver/internet.h
+++ b/src/knot/nameserver/internet.h
@@ -67,10 +67,10 @@ int internet_answer(knot_pkt_t *resp, struct query_data *qdata);
 
 /*! \brief Require authentication. */
 #define NS_NEED_AUTH(acl, qdata) \
-	if (!ns_proc_query_acl_check((acl), (qdata))) { \
+	if (!process_query_acl_check((acl), (qdata))) { \
 		return NS_PROC_FAIL; \
 	} else { \
-		if (ns_proc_query_verify(qdata) != KNOT_EOK) { \
+		if (process_query_verify(qdata) != KNOT_EOK) { \
 			return NS_PROC_FAIL; \
 		} \
 	}
diff --git a/src/knot/nameserver/ixfr.c b/src/knot/nameserver/ixfr.c
index 5bbd7f23b..3ca336e7f 100644
--- a/src/knot/nameserver/ixfr.c
+++ b/src/knot/nameserver/ixfr.c
@@ -3,7 +3,7 @@
 #include "knot/nameserver/ixfr.h"
 #include "knot/nameserver/axfr.h"
 #include "knot/nameserver/internet.h"
-#include "knot/nameserver/ns_proc_query.h"
+#include "knot/nameserver/process_query.h"
 #include "libknot/util/debug.h"
 #include "libknot/rdata.h"
 #include "knot/server/zones.h"
diff --git a/src/knot/nameserver/name-server.c b/src/knot/nameserver/name-server.c
index 0c64bcd86..7b91186cb 100644
--- a/src/knot/nameserver/name-server.c
+++ b/src/knot/nameserver/name-server.c
@@ -484,99 +484,3 @@ void knot_ns_destroy(knot_nameserver_t **nameserver)
 	free(*nameserver);
 	*nameserver = NULL;
 }
-
-/* State -> string translation table. */
-#ifdef KNOT_NS_DEBUG
-#define NS_STATE_STR(x) _state_table[x]
-static const char* _state_table[] = {
-        [NS_PROC_NOOP] = "NOOP",
-        [NS_PROC_MORE] = "MORE",
-        [NS_PROC_FULL] = "FULL",
-        [NS_PROC_DONE] = "DONE",
-        [NS_PROC_FAIL] = "FAIL"
-};
-#endif /* KNOT_NS_DEBUG */
-
-int ns_proc_begin(ns_proc_context_t *ctx, void *module_param, const ns_proc_module_t *module)
-{
-	/* Only in inoperable state. */
-	if (ctx->state != NS_PROC_NOOP) {
-		return NS_PROC_NOOP;
-	}
-
-#ifdef KNOT_NS_DEBUG
-	/* Check module API. */
-	assert(module->begin);
-	assert(module->in);
-	assert(module->out);
-	assert(module->err);
-	assert(module->reset);
-	assert(module->finish);
-#endif /* KNOT_NS_DEBUG */
-
-	ctx->module = module;
-	ctx->state = module->begin(ctx, module_param);
-
-	dbg_ns("%s -> %s\n", __func__, NS_STATE_STR(ctx->state));
-	return ctx->state;
-}
-
-int ns_proc_reset(ns_proc_context_t *ctx)
-{
-	ctx->state = ctx->module->reset(ctx);
-	dbg_ns("%s -> %s\n", __func__, NS_STATE_STR(ctx->state));
-	return ctx->state;
-}
-
-int ns_proc_finish(ns_proc_context_t *ctx)
-{
-	/* Only in operable state. */
-	if (ctx->state == NS_PROC_NOOP) {
-		return NS_PROC_NOOP;
-	}
-
-	ctx->state = ctx->module->finish(ctx);
-	dbg_ns("%s -> %s\n", __func__, NS_STATE_STR(ctx->state));
-	return ctx->state;
-}
-
-int ns_proc_in(const uint8_t *wire, uint16_t wire_len, ns_proc_context_t *ctx)
-{
-	/* Only if expecting data. */
-	if (ctx->state != NS_PROC_MORE) {
-		return NS_PROC_NOOP;
-	}
-
-	knot_pkt_t *pkt = knot_pkt_new((uint8_t *)wire, wire_len, &ctx->mm);
-	knot_pkt_parse(pkt, 0);
-
-	ctx->state = ctx->module->in(pkt, ctx);
-	dbg_ns("%s -> %s\n", __func__, NS_STATE_STR(ctx->state));
-	return ctx->state;
-}
-
-int ns_proc_out(uint8_t *wire, uint16_t *wire_len, ns_proc_context_t *ctx)
-{
-	knot_pkt_t *pkt = knot_pkt_new(wire, *wire_len, &ctx->mm);
-
-	switch(ctx->state) {
-	case NS_PROC_FULL: ctx->state = ctx->module->out(pkt, ctx); break;
-	case NS_PROC_FAIL: ctx->state = ctx->module->err(pkt, ctx); break;
-	default:
-		assert(0); /* Improper use. */
-		knot_pkt_free(&pkt);
-		return NS_PROC_NOOP;
-	}
-
-	/* Accept only finished result. */
-	if (ctx->state != NS_PROC_FAIL) {
-		*wire_len = pkt->size;
-	} else {
-		*wire_len = 0;
-	}
-
-	knot_pkt_free(&pkt);
-
-	dbg_ns("%s -> %s\n", __func__, NS_STATE_STR(ctx->state));
-	return ctx->state;
-}
diff --git a/src/knot/nameserver/name-server.h b/src/knot/nameserver/name-server.h
index 83d8f60e6..a754114bd 100644
--- a/src/knot/nameserver/name-server.h
+++ b/src/knot/nameserver/name-server.h
@@ -296,116 +296,6 @@ int knot_ns_tsig_required(int packet_nr);
  */
 void knot_ns_destroy(knot_nameserver_t **nameserver);
 
-/* ^^^
- * NG processing API below, everything upwards should be slowly moved to appropriate
- * files or removed.
- */
-
-/*! \brief Main packet processing states.
- *         Each state describes the current machine processing step
- *         and determines readiness for next action.
- */
-enum ns_proc_state {
-	NS_PROC_NOOP = 0,      /* N/A */
-	NS_PROC_MORE = 1 << 0, /* More input data. */
-	NS_PROC_FULL = 1 << 1, /* Has output data. */
-	NS_PROC_DONE = 1 << 2, /* Finished. */
-	NS_PROC_FAIL = 1 << 3  /* Error. */
-};
-
-/* Forward declarations. */
-struct ns_proc_module;
-
-/*! \brief Packte processing context. */
-typedef struct ns_proc_context
-{
-	int state;
-	mm_ctx_t mm;
-	uint16_t type;
-
-	knot_nameserver_t *ns;
-	void *data;
-
-	/* Module implementation. */
-	const struct ns_proc_module *module;
-} ns_proc_context_t;
-
-/*! \brief Packet processing module API. */
-typedef struct ns_proc_module {
-	int (*begin)(ns_proc_context_t *ctx, void *module_param);
-	int (*reset)(ns_proc_context_t *ctx);
-	int (*finish)(ns_proc_context_t *ctx);
-	int (*in)(knot_pkt_t *pkt, ns_proc_context_t *ctx);
-	int (*out)(knot_pkt_t *pkt, ns_proc_context_t *ctx);
-	int (*err)(knot_pkt_t *pkt, ns_proc_context_t *ctx);
-} ns_proc_module_t;
-
-/*! \brief Packet signing context.
- *  \todo This should be later moved to TSIG files when refactoring. */
-typedef struct ns_sign_context {
-	knot_tsig_key_t *tsig_key;
-	uint8_t *tsig_buf;
-	uint8_t *tsig_digest;
-	size_t tsig_buflen;
-	size_t tsig_digestlen;
-	uint8_t tsig_runlen;
-	uint64_t tsig_time_signed;
-	size_t pkt_count;
-} ns_sign_context_t;
-
-/*!
- * \brief Initialize packet processing context.
- *
- * Allowed from states: NOOP
- *
- * \param ctx Context.
- * \param module_param Parameters for given module.
- * \param module Module API.
- * \return (module specific state)
- */
-int ns_proc_begin(ns_proc_context_t *ctx, void *module_param, const ns_proc_module_t *module);
-
-/*!
- * \brief Reset current packet processing context.
- * \param ctx Context.
- * \return (module specific state)
- */
-int ns_proc_reset(ns_proc_context_t *ctx);
-
-/*!
- * \brief Finish and close packet processing context.
- *
- * Allowed from states: MORE, FULL, DONE, FAIL
- *
- * \param ctx Context.
- * \return (module specific state)
- */
-int ns_proc_finish(ns_proc_context_t *ctx);
-
-/*!
- * \brief Input more data into packet processing.
- *
- * Allowed from states: MORE
- *
- * \param wire Source data.
- * \param wire_len Source data length.
- * \param ctx Context.
- * \return (module specific state)
- */
-int ns_proc_in(const uint8_t *wire, uint16_t wire_len, ns_proc_context_t *ctx);
-
-/*!
- * \brief Write out output from packet processing.
- *
- * Allowed from states: FULL, FAIL
- *
- * \param wire Destination.
- * \param wire_len Destination length.
- * \param ctx Context.
- * \return (module specific state)
- */
-int ns_proc_out(uint8_t *wire, uint16_t *wire_len, ns_proc_context_t *ctx);
-
 #endif /* _KNOTNAME_SERVER_H_ */
 
 /*! @} */
diff --git a/src/knot/nameserver/nsec_proofs.c b/src/knot/nameserver/nsec_proofs.c
index 134733cbb..b5e832208 100644
--- a/src/knot/nameserver/nsec_proofs.c
+++ b/src/knot/nameserver/nsec_proofs.c
@@ -1,7 +1,7 @@
 #include <config.h>
 
 #include "knot/nameserver/nsec_proofs.h"
-#include "knot/nameserver/ns_proc_query.h"
+#include "knot/nameserver/process_query.h"
 
 #include "libknot/common.h"
 #include "libknot/rdata.h"
diff --git a/src/knot/nameserver/ns_proc_query.c b/src/knot/nameserver/process_query.c
similarity index 85%
rename from src/knot/nameserver/ns_proc_query.c
rename to src/knot/nameserver/process_query.c
index 44ea9fa87..eed6dfbb6 100644
--- a/src/knot/nameserver/ns_proc_query.c
+++ b/src/knot/nameserver/process_query.c
@@ -2,7 +2,7 @@
 #include <stdio.h>
 #include <urcu.h>
 
-#include "knot/nameserver/ns_proc_query.h"
+#include "knot/nameserver/process_query.h"
 #include "libknot/consts.h"
 #include "libknot/util/debug.h"
 #include "libknot/common.h"
@@ -23,38 +23,38 @@
 
 /* Forward decls. */
 static const knot_zone_t *answer_zone_find(const knot_pkt_t *pkt, knot_zonedb_t *zonedb);
-static int prepare_answer(const knot_pkt_t *query, knot_pkt_t *resp, ns_proc_context_t *ctx);
-static int query_internet(knot_pkt_t *pkt, ns_proc_context_t *ctx);
-static int query_chaos(knot_pkt_t *pkt, ns_proc_context_t *ctx);
-static int ratelimit_apply(int state, knot_pkt_t *pkt, ns_proc_context_t *ctx);
+static int prepare_answer(const knot_pkt_t *query, knot_pkt_t *resp, knot_process_t *ctx);
+static int query_internet(knot_pkt_t *pkt, knot_process_t *ctx);
+static int query_chaos(knot_pkt_t *pkt, knot_process_t *ctx);
+static int ratelimit_apply(int state, knot_pkt_t *pkt, knot_process_t *ctx);
 
 /*! \brief Module implementation. */
-const ns_proc_module_t _ns_proc_query = {
-        &ns_proc_query_begin,
-        &ns_proc_query_reset,
-        &ns_proc_query_finish,
-        &ns_proc_query_in,
-        &ns_proc_query_out,
-        &ns_proc_query_err
+const knot_process_module_t _process_query = {
+        &process_query_begin,
+        &process_query_reset,
+        &process_query_finish,
+        &process_query_in,
+        &process_query_out,
+        &process_query_err
 };
 
 /*! \brief Accessor to query-specific data. */
 #define QUERY_DATA(ctx) ((struct query_data *)(ctx)->data)
 
 /*! \brief Reinitialize query data structure. */
-static void query_data_init(ns_proc_context_t *ctx, void *module_param)
+static void query_data_init(knot_process_t *ctx, void *module_param)
 {
 	/* Initialize persistent data. */
 	struct query_data *data = QUERY_DATA(ctx);
 	memset(data, 0, sizeof(struct query_data));
 	data->mm = &ctx->mm;
-	data->param = (struct ns_proc_query_param*)module_param;
+	data->param = (struct process_query_param*)module_param;
 
 	/* Initialize list. */
 	init_list(&data->wildcards);
 }
 
-int ns_proc_query_begin(ns_proc_context_t *ctx, void *module_param)
+int process_query_begin(knot_process_t *ctx, void *module_param)
 {
 	/* Initialize context. */
 	assert(ctx);
@@ -68,13 +68,13 @@ int ns_proc_query_begin(ns_proc_context_t *ctx, void *module_param)
 	return NS_PROC_MORE;
 }
 
-int ns_proc_query_reset(ns_proc_context_t *ctx)
+int process_query_reset(knot_process_t *ctx)
 {
 	assert(ctx);
 	struct query_data *qdata = QUERY_DATA(ctx);
 
 	/* Remember persistent parameters. */
-	struct ns_proc_query_param *module_param = qdata->param;
+	struct process_query_param *module_param = qdata->param;
 
 	/* Free allocated data. */
 	knot_pkt_free(&qdata->query);
@@ -89,15 +89,15 @@ int ns_proc_query_reset(ns_proc_context_t *ctx)
 	/* Await packet. */
 	return NS_PROC_MORE;
 }
-int ns_proc_query_finish(ns_proc_context_t *ctx)
+int process_query_finish(knot_process_t *ctx)
 {
-	ns_proc_query_reset(ctx);
+	process_query_reset(ctx);
 	ctx->mm.free(ctx->data);
 	ctx->data = NULL;
 
 	return NS_PROC_NOOP;
 }
-int ns_proc_query_in(knot_pkt_t *pkt, ns_proc_context_t *ctx)
+int process_query_in(knot_pkt_t *pkt, knot_process_t *ctx)
 {
 	assert(pkt && ctx);
 	struct query_data *qdata = QUERY_DATA(ctx);
@@ -122,7 +122,7 @@ int ns_proc_query_in(knot_pkt_t *pkt, ns_proc_context_t *ctx)
 	return NS_PROC_FULL;
 }
 
-int ns_proc_query_out(knot_pkt_t *pkt, ns_proc_context_t *ctx)
+int process_query_out(knot_pkt_t *pkt, knot_process_t *ctx)
 {
 	assert(pkt && ctx);
 	struct query_data *qdata = QUERY_DATA(ctx);
@@ -178,7 +178,7 @@ int ns_proc_query_out(knot_pkt_t *pkt, ns_proc_context_t *ctx)
 
 	/* Transaction security (if applicable). */
 	if (next_state == NS_PROC_DONE || next_state == NS_PROC_FULL) {
-		if (ns_proc_query_sign_response(pkt, qdata) != KNOT_EOK) {
+		if (process_query_sign_response(pkt, qdata) != KNOT_EOK) {
 			next_state = NS_PROC_FAIL;
 		}
 	}
@@ -192,7 +192,7 @@ finish:
 	return next_state;
 }
 
-int ns_proc_query_err(knot_pkt_t *pkt, ns_proc_context_t *ctx)
+int process_query_err(knot_pkt_t *pkt, knot_process_t *ctx)
 {
 	assert(pkt && ctx);
 	struct query_data *qdata = QUERY_DATA(ctx);
@@ -214,14 +214,14 @@ int ns_proc_query_err(knot_pkt_t *pkt, ns_proc_context_t *ctx)
 	knot_wire_set_rcode(pkt->wire, qdata->rcode);
 
 	/* Transaction security (if applicable). */
-	if (ns_proc_query_sign_response(pkt, qdata) != KNOT_EOK) {
+	if (process_query_sign_response(pkt, qdata) != KNOT_EOK) {
 		return NS_PROC_FAIL;
 	}
 
 	return NS_PROC_DONE;
 }
 
-bool ns_proc_query_acl_check(acl_t *acl, struct query_data *qdata)
+bool process_query_acl_check(acl_t *acl, struct query_data *qdata)
 {
 	knot_pkt_t *query = qdata->query;
 	const sockaddr_t *query_source = &qdata->param->query_source;
@@ -253,10 +253,10 @@ bool ns_proc_query_acl_check(acl_t *acl, struct query_data *qdata)
 	return true;
 }
 
-int ns_proc_query_verify(struct query_data *qdata)
+int process_query_verify(struct query_data *qdata)
 {
 	knot_pkt_t *query = qdata->query;
-	ns_sign_context_t *ctx = &qdata->sign;
+	knot_sign_context_t *ctx = &qdata->sign;
 
 	/* NOKEY => no verification. */
 	if (query->tsig_rr == NULL) {
@@ -303,11 +303,11 @@ int ns_proc_query_verify(struct query_data *qdata)
 	return ret;
 }
 
-int ns_proc_query_sign_response(knot_pkt_t *pkt, struct query_data *qdata)
+int process_query_sign_response(knot_pkt_t *pkt, struct query_data *qdata)
 {
 	int ret = KNOT_EOK;
 	knot_pkt_t *query = qdata->query;
-	ns_sign_context_t *ctx = &qdata->sign;
+	knot_sign_context_t *ctx = &qdata->sign;
 
 	/* KEY provided and verified TSIG or BADTIME allows signing. */
 	if (ctx->tsig_key != NULL && knot_tsig_can_sign(qdata->rcode_tsig)) {
@@ -358,9 +358,10 @@ fail:
 /*!
  * \brief Create a response for a given query in the INTERNET class.
  */
-static int query_internet(knot_pkt_t *pkt, ns_proc_context_t *ctx)
+static int query_internet(knot_pkt_t *pkt, knot_process_t *ctx)
 {
 	struct query_data *data = QUERY_DATA(ctx);
+	knot_nameserver_t *ns = data->param->ns;
 	int next_state = NS_PROC_FAIL;
 	dbg_ns("%s(%p, %p, pkt_type=%u)\n", __func__, pkt, ctx, data->packet_type);
 
@@ -369,16 +370,16 @@ static int query_internet(knot_pkt_t *pkt, ns_proc_context_t *ctx)
 		next_state = internet_answer(pkt, data);
 		break;
 	case KNOT_QUERY_NOTIFY:
-		next_state = internet_notify(pkt, ctx->ns, data);
+		next_state = internet_notify(pkt, ns, data);
 		break;
 	case KNOT_QUERY_AXFR:
-		next_state = axfr_answer(pkt, ctx->ns, data);
+		next_state = axfr_answer(pkt, ns, data);
 		break;
 	case KNOT_QUERY_IXFR:
-		next_state = ixfr_answer(pkt, ctx->ns, data);
+		next_state = ixfr_answer(pkt, ns, data);
 		break;
 	case KNOT_QUERY_UPDATE:
-		next_state = update_answer(pkt, ctx->ns, data);
+		next_state = update_answer(pkt, ns, data);
 		break;
 	default:
 		/* Nothing else is supported. */
@@ -393,11 +394,11 @@ static int query_internet(knot_pkt_t *pkt, ns_proc_context_t *ctx)
 /*!
  * \brief Apply rate limit.
  */
-static int ratelimit_apply(int state, knot_pkt_t *pkt, ns_proc_context_t *ctx)
+static int ratelimit_apply(int state, knot_pkt_t *pkt, knot_process_t *ctx)
 {
 	/* Check if rate limiting applies. */
 	struct query_data *qdata = QUERY_DATA(ctx);
-	server_t *server = (server_t *)ctx->ns->data;
+	server_t *server = (server_t *)qdata->param->ns->data;
 	if (server->rrl == NULL) {
 		return state;
 	}
@@ -417,7 +418,7 @@ static int ratelimit_apply(int state, knot_pkt_t *pkt, ns_proc_context_t *ctx)
 	/* Now it is slip or drop. */
 	if (rrl_slip_roll(conf()->rrl_slip)) {
 		/* Answer slips. */
-		if (ns_proc_query_err(pkt, ctx) != KNOT_EOK) {
+		if (process_query_err(pkt, ctx) != KNOT_EOK) {
 			return NS_PROC_FAIL;
 		}
 		knot_wire_set_tc(pkt->wire);
@@ -432,7 +433,7 @@ static int ratelimit_apply(int state, knot_pkt_t *pkt, ns_proc_context_t *ctx)
 /*!
  * \brief Create a response for a given query in the CHAOS class.
  */
-static int query_chaos(knot_pkt_t *pkt, ns_proc_context_t *ctx)
+static int query_chaos(knot_pkt_t *pkt, knot_process_t *ctx)
 {
 	dbg_ns("%s(%p, %p)\n", __func__, pkt, ctx);
 	struct query_data *data = QUERY_DATA(ctx);
@@ -443,7 +444,7 @@ static int query_chaos(knot_pkt_t *pkt, ns_proc_context_t *ctx)
 		return NS_PROC_FAIL;
 	}
 
-	data->rcode = knot_chaos_answer(pkt, ctx->ns);
+	data->rcode = knot_chaos_answer(pkt, data->param->ns);
 	if (data->rcode != KNOT_RCODE_NOERROR) {
 		dbg_ns("%s: failed with RCODE=%d\n", __func__, data->rcode);
 		return NS_PROC_FAIL;
@@ -487,7 +488,7 @@ static const knot_zone_t *answer_zone_find(const knot_pkt_t *pkt, knot_zonedb_t
 }
 
 /*! \brief Initialize response, sizes and find zone from which we're going to answer. */
-static int prepare_answer(const knot_pkt_t *query, knot_pkt_t *resp, ns_proc_context_t *ctx)
+static int prepare_answer(const knot_pkt_t *query, knot_pkt_t *resp, knot_process_t *ctx)
 {
 	int ret = knot_pkt_init_response(resp, query);
 	if (ret != KNOT_EOK) {
@@ -499,6 +500,7 @@ static int prepare_answer(const knot_pkt_t *query, knot_pkt_t *resp, ns_proc_con
 	 * Already checked for absence of compression and length.
 	 */
 	struct query_data *qdata = QUERY_DATA(ctx);
+	knot_nameserver_t *ns = qdata->param->ns;
 	const knot_dname_t *qname = knot_pkt_qname(query);
 	memcpy(qdata->orig_qname, qname, query->qname_size);
 	ret = knot_dname_to_lower((knot_dname_t *)qname);
@@ -508,7 +510,7 @@ static int prepare_answer(const knot_pkt_t *query, knot_pkt_t *resp, ns_proc_con
 	}
 
 	/* Find zone for QNAME. */
-	qdata->zone = answer_zone_find(query, ctx->ns->zone_db);
+	qdata->zone = answer_zone_find(query, ns->zone_db);
 
 	/* Update maximal answer size. */
 	if (qdata->param->proc_flags & NS_QUERY_LIMIT_SIZE) {
@@ -519,7 +521,7 @@ static int prepare_answer(const knot_pkt_t *query, knot_pkt_t *resp, ns_proc_con
 	if (!knot_pkt_have_edns(query)) {
 		return KNOT_EOK;
 	}
-	ret = knot_pkt_add_opt(resp, ctx->ns->opt_rr, knot_pkt_have_nsid(query));
+	ret = knot_pkt_add_opt(resp, ns->opt_rr, knot_pkt_have_nsid(query));
 	if (ret != KNOT_EOK) {
 		dbg_ns("%s: can't add OPT RR (%d)\n", __func__, ret);
 		return ret;
diff --git a/src/knot/nameserver/ns_proc_query.h b/src/knot/nameserver/process_query.h
similarity index 82%
rename from src/knot/nameserver/ns_proc_query.h
rename to src/knot/nameserver/process_query.h
index 5aad3a36e..2ab422923 100644
--- a/src/knot/nameserver/ns_proc_query.h
+++ b/src/knot/nameserver/process_query.h
@@ -1,5 +1,5 @@
 /*!
- * \file ns_proc_query.h
+ * \file process_query.h
  *
  * \author Marek Vavrusa <marek.vavrusa@nic.cz>
  *
@@ -24,15 +24,16 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef _KNOT_NS_PROC_QUERY_H_
-#define _KNOT_NS_PROC_QUERY_H_
+#ifndef _PROCESS_QUERY_H_
+#define _PROCESS_QUERY_H_
 
+#include "libknot/processing/process.h"
 #include "knot/nameserver/name-server.h"
 #include "common/acl.h"
 
 /* Query processing module implementation. */
-extern const ns_proc_module_t _ns_proc_query;
-#define NS_PROC_QUERY (&_ns_proc_query)
+extern const knot_process_module_t _process_query;
+#define NS_PROC_QUERY (&_process_query)
 #define NS_PROC_QUERY_ID 1
 
 /*! \brief Query processing logging common base. */
@@ -56,7 +57,7 @@ extern const ns_proc_module_t _ns_proc_query;
 	NS_PROC_LOG(severity, qdata, what " of '%s' to '%s:%d': ", msg)
 
 /* Query processing specific flags. */
-enum ns_proc_query_flag {
+enum process_query_flag {
 	NS_QUERY_NO_AXFR    = 1 << 0, /* Don't process AXFR */
 	NS_QUERY_NO_IXFR    = 1 << 1, /* Don't process IXFR */
 	NS_QUERY_LIMIT_ANY  = 1 << 2, /* Limit ANY QTYPE (respond with TC=1) */
@@ -65,9 +66,10 @@ enum ns_proc_query_flag {
 };
 
 /* Module load parameters. */
-struct ns_proc_query_param {
+struct process_query_param {
 	uint16_t   proc_flags;
 	sockaddr_t query_source;
+	knot_nameserver_t *ns;
 };
 
 /*! \brief Query processing intermediate data. */
@@ -89,10 +91,10 @@ struct query_data {
 	/* Extensions. */
 	void *ext;
 	void (*ext_cleanup)(struct query_data*); /*!< Extensions cleanup callback. */
-	ns_sign_context_t sign;            /*!< Signing context. */
+	knot_sign_context_t sign;            /*!< Signing context. */
 
 	/* Everything below should be kept on reset. */
-	struct ns_proc_query_param *param; /*!< Module parameters. */
+	struct process_query_param *param; /*!< Module parameters. */
 	mm_ctx_t *mm;                      /*!< Memory context. */
 };
 
@@ -110,7 +112,7 @@ struct wildcard_hit {
  * \param module_param
  * \return MORE (awaits query)
  */
-int ns_proc_query_begin(ns_proc_context_t *ctx, void *module_param);
+int process_query_begin(knot_process_t *ctx, void *module_param);
 
 /*!
  * \brief Reset query processing context.
@@ -118,7 +120,7 @@ int ns_proc_query_begin(ns_proc_context_t *ctx, void *module_param);
  * \param ctx
  * \return MORE (awaits next query)
  */
-int ns_proc_query_reset(ns_proc_context_t *ctx);
+int process_query_reset(knot_process_t *ctx);
 
 /*!
  * \brief Finish and close current query processing.
@@ -126,7 +128,7 @@ int ns_proc_query_reset(ns_proc_context_t *ctx);
  * \param ctx
  * \return NOOP (context will be inoperable further on)
  */
-int ns_proc_query_finish(ns_proc_context_t *ctx);
+int process_query_finish(knot_process_t *ctx);
 
 /*!
  * \brief Put query into query processing context.
@@ -136,7 +138,7 @@ int ns_proc_query_finish(ns_proc_context_t *ctx);
  * \retval NOOP (unsupported query)
  * \retval FULL (ready to write answer)
  */
-int ns_proc_query_in(knot_pkt_t *pkt, ns_proc_context_t *ctx);
+int process_query_in(knot_pkt_t *pkt, knot_process_t *ctx);
 
 /*!
  * \brief Make query response.
@@ -147,7 +149,7 @@ int ns_proc_query_in(knot_pkt_t *pkt, ns_proc_context_t *ctx);
  * \retval FULL (partial response, send it and call again)
  * \retval FAIL (failure)
  */
-int ns_proc_query_out(knot_pkt_t *pkt, ns_proc_context_t *ctx);
+int process_query_out(knot_pkt_t *pkt, knot_process_t *ctx);
 
 /*!
  * \brief Make an error response.
@@ -157,7 +159,7 @@ int ns_proc_query_out(knot_pkt_t *pkt, ns_proc_context_t *ctx);
  * \retval DONE (finished response)
  * \retval FAIL (failure)
  */
-int ns_proc_query_err(knot_pkt_t *pkt, ns_proc_context_t *ctx);
+int process_query_err(knot_pkt_t *pkt, knot_process_t *ctx);
 
 /*!
  * \brief Check current query against ACL.
@@ -166,7 +168,7 @@ int ns_proc_query_err(knot_pkt_t *pkt, ns_proc_context_t *ctx);
  * \param qdata
  * \return true if accepted, false if denied.
  */
-bool ns_proc_query_acl_check(acl_t *acl, struct query_data *qdata);
+bool process_query_acl_check(acl_t *acl, struct query_data *qdata);
 
 /*!
  * \brief Verify current query transaction security and update query data.
@@ -178,7 +180,7 @@ bool ns_proc_query_acl_check(acl_t *acl, struct query_data *qdata);
  * \retval KNOT_TSIG_EBADTIME
  * \retval (other generic errors)
  */
-int ns_proc_query_verify(struct query_data *qdata);
+int process_query_verify(struct query_data *qdata);
 
 /*!
  * \brief Sign query response if applicable.
@@ -188,8 +190,8 @@ int ns_proc_query_verify(struct query_data *qdata);
  * \retval KNOT_EOK
  * \retval (other generic errors)
  */
-int ns_proc_query_sign_response(knot_pkt_t *pkt, struct query_data *qdata);
+int process_query_sign_response(knot_pkt_t *pkt, struct query_data *qdata);
 
-#endif /* _KNOT_NS_PROC_QUERY_H_ */
+#endif /* _PROCESS_QUERY_H_ */
 
 /*! @} */
diff --git a/src/knot/nameserver/update.c b/src/knot/nameserver/update.c
index f56927c7d..b8b3e6e29 100644
--- a/src/knot/nameserver/update.c
+++ b/src/knot/nameserver/update.c
@@ -2,7 +2,7 @@
 
 #include "knot/nameserver/update.h"
 #include "knot/nameserver/internet.h"
-#include "knot/nameserver/ns_proc_query.h"
+#include "knot/nameserver/process_query.h"
 #include "knot/nameserver/name-server.h"
 #include "libknot/util/debug.h"
 #include "knot/dnssec/zone-events.h"
diff --git a/src/knot/server/notify.c b/src/knot/server/notify.c
index 1b16f4a8a..afd23506c 100644
--- a/src/knot/server/notify.c
+++ b/src/knot/server/notify.c
@@ -35,7 +35,7 @@
 #include "libknot/rdata.h"
 #include "knot/nameserver/internet.h"
 #include "libknot/util/debug.h"
-#include "knot/nameserver/ns_proc_query.h"
+#include "knot/nameserver/process_query.h"
 #include "libknot/dnssec/random.h"
 
 /*----------------------------------------------------------------------------*/
diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c
index a86c79d38..336396dbf 100644
--- a/src/knot/server/tcp-handler.c
+++ b/src/knot/server/tcp-handler.c
@@ -41,13 +41,14 @@
 #include "knot/server/zones.h"
 #include "knot/nameserver/name-server.h"
 #include "libknot/packet/wire.h"
-#include "knot/nameserver/ns_proc_query.h"
+#include "knot/nameserver/process_query.h"
 #include "libknot/dnssec/crypto.h"
 #include "libknot/dnssec/random.h"
 
 /*! \brief TCP context data. */
-typedef struct tcp_context_t {
-	ns_proc_context_t query_ctx; /*!< Query processing context. */
+typedef struct tcp_context {
+	knot_process_t query_ctx;    /*!< Query processing context. */
+	knot_nameserver_t *ns;       /*!< Name server structure. */
 	struct iovec iov[2];         /*!< TX/RX buffers. */
 	unsigned client_threshold;   /*!< Index of first TCP client. */
 	timev_t last_poll_time;      /*!< Time of the last socket poll. */
@@ -104,12 +105,13 @@ static enum fdset_sweep_state tcp_sweep(fdset_t *set, int i, void *data)
 /*!
  * \brief TCP event handler function.
  */
-static int tcp_handle(ns_proc_context_t *query_ctx, int fd,
+static int tcp_handle(tcp_context_t *tcp, int fd,
                       struct iovec *rx, struct iovec *tx)
 {
 	/* Create query processing parameter. */
-	struct ns_proc_query_param param = {0};
+	struct process_query_param param = {0};
 	sockaddr_prep(&param.query_source);
+	param.ns = tcp->ns;
 	rx->iov_len = KNOT_WIRE_MAX_PKTSIZE;
 	tx->iov_len = KNOT_WIRE_MAX_PKTSIZE;
 
@@ -133,16 +135,16 @@ static int tcp_handle(ns_proc_context_t *query_ctx, int fd,
 	}
 
 	/* Create query processing context. */
-	ns_proc_begin(query_ctx, &param, NS_PROC_QUERY);
+	knot_process_begin(&tcp->query_ctx, &param, NS_PROC_QUERY);
 
 	/* Input packet. */
-	int state = ns_proc_in(rx->iov_base, rx->iov_len, query_ctx);
+	int state = knot_process_in(rx->iov_base, rx->iov_len, &tcp->query_ctx);
 
 	/* Resolve until NOOP or finished. */
 	ret = KNOT_EOK;
 	while (state & (NS_PROC_FULL|NS_PROC_FAIL)) {
 		uint16_t tx_len = tx->iov_len;
-		state = ns_proc_out(tx->iov_base, &tx_len, query_ctx);
+		state = knot_process_out(tx->iov_base, &tx_len, &tcp->query_ctx);
 
 		/* If it has response, send it. */
 		if (tx_len > 0) {
@@ -154,7 +156,7 @@ static int tcp_handle(ns_proc_context_t *query_ctx, int fd,
 	}
 
 	/* Reset after processing. */
-	ns_proc_finish(query_ctx);
+	knot_process_finish(&tcp->query_ctx);
 
 	return ret;
 }
@@ -298,7 +300,7 @@ static int tcp_event_accept(tcp_context_t *tcp, unsigned i)
 static int tcp_event_serve(tcp_context_t *tcp, unsigned i)
 {
 	int fd = tcp->set.pfd[i].fd;
-	int ret = tcp_handle(&tcp->query_ctx, fd, &tcp->iov[0], &tcp->iov[1]);
+	int ret = tcp_handle(tcp, fd, &tcp->iov[0], &tcp->iov[1]);
 
 	/* Flush per-query memory. */
 	mp_flush(tcp->query_ctx.mm.ctx);
@@ -375,7 +377,7 @@ int tcp_master(dthread_t *thread)
 	memset(&tcp, 0, sizeof(tcp_context_t));
 
 	/* Create TCP answering context. */
-	tcp.query_ctx.ns = handler->server->nameserver;
+	tcp.ns = handler->server->nameserver;
 
 	/* Create big enough memory cushion. */
 	mm_ctx_mempool(&tcp.query_ctx.mm, 4 * sizeof(knot_pkt_t));
diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c
index baa20faa0..de9dbe233 100644
--- a/src/knot/server/udp-handler.c
+++ b/src/knot/server/udp-handler.c
@@ -51,6 +51,7 @@
 #include "knot/server/zones.h"
 #include "knot/server/notify.h"
 #include "libknot/dnssec/crypto.h"
+#include "libknot/processing/process.h"
 
 /* Buffer identifiers. */
 enum {
@@ -59,6 +60,12 @@ enum {
 	NBUFS = 2
 };
 
+/*! \brief UDP context data. */
+typedef struct udp_context {
+	knot_process_t query_ctx;    /*!< Query processing context. */
+	knot_nameserver_t *ns;       /*!< Name server structure. */
+} udp_context_t;
+
 /* FD_COPY macro compat. */
 #ifndef FD_COPY
 #define FD_COPY(src, dest) memcpy((dest), (src), sizeof(fd_set))
@@ -73,7 +80,7 @@ enum {
 /* Next-gen packet processing API. */
 #define PACKET_NG
 #ifdef PACKET_NG
-#include "knot/nameserver/ns_proc_query.h"
+#include "knot/nameserver/process_query.h"
 #endif
 
 /* PPS measurement */
@@ -105,7 +112,7 @@ static inline void udp_pps_begin() {}
 static inline void udp_pps_sample(unsigned n, unsigned thr_id) {}
 #endif
 
-int udp_handle(ns_proc_context_t *query_ctx, int fd, sockaddr_t *addr,
+int udp_handle(udp_context_t *udp, int fd, sockaddr_t *addr,
                struct iovec *rx, struct iovec *tx)
 {
 #ifdef DEBUG_ENABLE_BRIEF
@@ -117,34 +124,35 @@ int udp_handle(ns_proc_context_t *query_ctx, int fd, sockaddr_t *addr,
 #endif
 
 	/* Create query processing parameter. */
-	struct ns_proc_query_param param = {0};
+	struct process_query_param param = {0};
 	sockaddr_copy(&param.query_source, addr);
 	param.proc_flags  = NS_QUERY_NO_AXFR|NS_QUERY_NO_IXFR; /* No transfers. */
 	param.proc_flags |= NS_QUERY_LIMIT_SIZE; /* Enforce UDP packet size limit. */
 	param.proc_flags |= NS_QUERY_LIMIT_ANY;  /* Limit ANY over UDP (depends on zone as well). */
+	param.ns = NULL;
 
 	/* Rate limit is applied? */
-	server_t *server = (server_t *)query_ctx->ns->data;
+	server_t *server = (server_t *)udp->ns->data;
 	if (knot_unlikely(server->rrl != NULL) && server->rrl->rate > 0) {
 		param.proc_flags |= NS_QUERY_LIMIT_RATE;
 	}
 
 	/* Create query processing context. */
-	ns_proc_begin(query_ctx, &param, NS_PROC_QUERY);
+	knot_process_begin(&udp->query_ctx, &param, NS_PROC_QUERY);
 
 	/* Input packet. */
-	int state = ns_proc_in(rx->iov_base, rx->iov_len, query_ctx);
+	int state = knot_process_in(rx->iov_base, rx->iov_len, &udp->query_ctx);
 
 	/* Process answer. */
 	uint16_t tx_len = tx->iov_len;
 	if (state == NS_PROC_FULL) {
-		state = ns_proc_out(tx->iov_base, &tx_len, query_ctx);
+		state = knot_process_out(tx->iov_base, &tx_len, &udp->query_ctx);
 	}
 
 	/* Process error response (if failed). */
 	if (state == NS_PROC_FAIL) {
 		tx_len = tx->iov_len; /* Reset size. */
-		state = ns_proc_out(tx->iov_base, &tx_len, query_ctx);
+		state = knot_process_out(tx->iov_base, &tx_len, &udp->query_ctx);
 	}
 
 	/* Send response only if finished successfuly. */
@@ -155,7 +163,7 @@ int udp_handle(ns_proc_context_t *query_ctx, int fd, sockaddr_t *addr,
 	}
 
 	/* Reset context. */
-	ns_proc_finish(query_ctx);
+	knot_process_finish(&udp->query_ctx);
 	return KNOT_EOK;
 }
 
@@ -172,7 +180,7 @@ int udp_handle(ns_proc_context_t *query_ctx, int fd, sockaddr_t *addr,
 static void* (*_udp_init)(void) = 0;
 static int (*_udp_deinit)(void *) = 0;
 static int (*_udp_recv)(int, void *) = 0;
-static int (*_udp_handle)(ns_proc_context_t *, void *) = 0;
+static int (*_udp_handle)(udp_context_t *, void *) = 0;
 static int (*_udp_send)(void *) = 0;
 
 /* UDP recvfrom() request struct. */
@@ -227,7 +235,7 @@ static int udp_recvfrom_recv(int fd, void *d)
 	return 0;
 }
 
-static int udp_recvfrom_handle(ns_proc_context_t *ctx, void *d)
+static int udp_recvfrom_handle(udp_context_t *ctx, void *d)
 {
 	struct udp_recvfrom *rq = (struct udp_recvfrom *)d;
 
@@ -378,7 +386,7 @@ static int udp_recvmmsg_recv(int fd, void *d)
 	return n;
 }
 
-static int udp_recvmmsg_handle(ns_proc_context_t *ctx, void *d)
+static int udp_recvmmsg_handle(udp_context_t *ctx, void *d)
 {
 	struct udp_recvmmsg *rq = (struct udp_recvmmsg *)d;
 
@@ -482,12 +490,12 @@ int udp_master(dthread_t *thread)
 	ifacelist_t *ref = NULL;
 
 	/* Create UDP answering context. */
-	ns_proc_context_t query_ctx;
-	memset(&query_ctx, 0, sizeof(query_ctx));
-	query_ctx.ns = handler->server->nameserver;
+	udp_context_t udp;
+	memset(&udp, 0, sizeof(udp_context_t));
+	udp.ns = handler->server->nameserver;
 
 	/* Create big enough memory cushion. */
-	mm_ctx_mempool(&query_ctx.mm, 4 * sizeof(knot_pkt_t));
+	mm_ctx_mempool(&udp.query_ctx.mm, 4 * sizeof(knot_pkt_t));
 
 	/* Chose select as epoll/kqueue has larger overhead for a
 	 * single or handful of sockets. */
@@ -540,9 +548,9 @@ int udp_master(dthread_t *thread)
 		for (unsigned fd = minfd; fd <= maxfd; ++fd) {
 			if (FD_ISSET(fd, &rfds)) {
 				while ((rcvd = _udp_recv(fd, rq)) > 0) {
-					_udp_handle(&query_ctx, rq);
+					_udp_handle(&udp, rq);
 					/* Flush allocated memory. */
-					mp_flush(query_ctx.mm.ctx);
+					mp_flush(udp.query_ctx.mm.ctx);
 					_udp_send(rq);
 					udp_pps_sample(rcvd, thr_id);
 				}
@@ -552,7 +560,7 @@ int udp_master(dthread_t *thread)
 
 	_udp_deinit(rq);
 	ref_release((ref_t *)ref);
-	mp_delete(query_ctx.mm.ctx);
+	mp_delete(udp.query_ctx.mm.ctx);
 	return KNOT_EOK;
 }
 
diff --git a/src/libknot/processing/process.c b/src/libknot/processing/process.c
new file mode 100644
index 000000000..91abc3a1b
--- /dev/null
+++ b/src/libknot/processing/process.c
@@ -0,0 +1,119 @@
+/*  Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <assert.h>
+
+#include "libknot/processing/process.h"
+#include "libknot/util/debug.h"
+
+/* State -> string translation table. */
+#ifdef KNOT_NS_DEBUG
+#define PROCESSING_STATE_STR(x) _state_table[x]
+static const char* _state_table[] = {
+        [NS_PROC_NOOP] = "NOOP",
+        [NS_PROC_MORE] = "MORE",
+        [NS_PROC_FULL] = "FULL",
+        [NS_PROC_DONE] = "DONE",
+        [NS_PROC_FAIL] = "FAIL"
+};
+#endif /* KNOT_NS_DEBUG */
+
+int knot_process_begin(knot_process_t *ctx, void *module_param, const knot_process_module_t *module)
+{
+	/* Only in inoperable state. */
+	if (ctx->state != NS_PROC_NOOP) {
+		return NS_PROC_NOOP;
+	}
+
+#ifdef KNOT_NS_DEBUG
+	/* Check module API. */
+	assert(module->begin);
+	assert(module->in);
+	assert(module->out);
+	assert(module->err);
+	assert(module->reset);
+	assert(module->finish);
+#endif /* KNOT_NS_DEBUG */
+
+	ctx->module = module;
+	ctx->state = module->begin(ctx, module_param);
+
+	dbg_ns("%s -> %s\n", __func__, PROCESSING_STATE_STR(ctx->state));
+	return ctx->state;
+}
+
+int knot_process_reset(knot_process_t *ctx)
+{
+	ctx->state = ctx->module->reset(ctx);
+	dbg_ns("%s -> %s\n", __func__, PROCESSING_STATE_STR(ctx->state));
+	return ctx->state;
+}
+
+int knot_process_finish(knot_process_t *ctx)
+{
+	/* Only in operable state. */
+	if (ctx->state == NS_PROC_NOOP) {
+		return NS_PROC_NOOP;
+	}
+
+	ctx->state = ctx->module->finish(ctx);
+	dbg_ns("%s -> %s\n", __func__, PROCESSING_STATE_STR(ctx->state));
+	return ctx->state;
+}
+
+int knot_process_in(const uint8_t *wire, uint16_t wire_len, knot_process_t *ctx)
+{
+	/* Only if expecting data. */
+	if (ctx->state != NS_PROC_MORE) {
+		return NS_PROC_NOOP;
+	}
+
+	knot_pkt_t *pkt = knot_pkt_new((uint8_t *)wire, wire_len, &ctx->mm);
+	knot_pkt_parse(pkt, 0);
+
+	ctx->state = ctx->module->in(pkt, ctx);
+	dbg_ns("%s -> %s\n", __func__, PROCESSING_STATE_STR(ctx->state));
+	return ctx->state;
+}
+
+int knot_process_out(uint8_t *wire, uint16_t *wire_len, knot_process_t *ctx)
+{
+	knot_pkt_t *pkt = knot_pkt_new(wire, *wire_len, &ctx->mm);
+
+	switch(ctx->state) {
+	case NS_PROC_FULL: ctx->state = ctx->module->out(pkt, ctx); break;
+	case NS_PROC_FAIL: ctx->state = ctx->module->err(pkt, ctx); break;
+	default:
+		assert(0); /* Improper use. */
+		knot_pkt_free(&pkt);
+		return NS_PROC_NOOP;
+	}
+
+	/* Accept only finished result. */
+	if (ctx->state != NS_PROC_FAIL) {
+		*wire_len = pkt->size;
+	} else {
+		*wire_len = 0;
+	}
+
+	knot_pkt_free(&pkt);
+
+	dbg_ns("%s -> %s\n", __func__, PROCESSING_STATE_STR(ctx->state));
+	return ctx->state;
+}
+
+#undef PROCESSING_STATE_STR
diff --git a/src/libknot/processing/process.h b/src/libknot/processing/process.h
new file mode 100644
index 000000000..8be678a11
--- /dev/null
+++ b/src/libknot/processing/process.h
@@ -0,0 +1,127 @@
+/*!
+ * \file process.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \addtogroup query_processing
+ * @{
+ */
+/*  Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KNOT_PROCESS_H
+#define _KNOT_PROCESS_H
+
+#include <stdint.h>
+
+#include "common/mempattern.h"
+#include "libknot/consts.h"
+#include "libknot/tsig.h"
+#include "libknot/packet/pkt.h"
+
+/*! \brief Main packet processing states.
+ *         Each state describes the current machine processing step
+ *         and determines readiness for next action.
+ */
+enum knot_process_state {
+	NS_PROC_NOOP = 0,      /* N/A */
+	NS_PROC_MORE = 1 << 0, /* More input data. */
+	NS_PROC_FULL = 1 << 1, /* Has output data. */
+	NS_PROC_DONE = 1 << 2, /* Finished. */
+	NS_PROC_FAIL = 1 << 3  /* Error. */
+};
+
+/* Forward declarations. */
+struct knot_process_module;
+
+/*! \brief Packet processing context. */
+typedef struct knot_process_context
+{
+	uint16_t state;  /* Bitmap of enum knot_process_state. */
+	uint16_t type;   /* Module identifier. */
+	mm_ctx_t mm;     /* Processing memory context. */
+
+	/* Module specific. */
+	void *data;
+	const struct knot_process_module *module;
+} knot_process_t;
+
+/*! \brief Packet processing module API. */
+typedef struct knot_process_module {
+	int (*begin)(knot_process_t *ctx, void *module_param);
+	int (*reset)(knot_process_t *ctx);
+	int (*finish)(knot_process_t *ctx);
+	int (*in)(knot_pkt_t *pkt, knot_process_t *ctx);
+	int (*out)(knot_pkt_t *pkt, knot_process_t *ctx);
+	int (*err)(knot_pkt_t *pkt, knot_process_t *ctx);
+} knot_process_module_t;
+
+/*!
+ * \brief Initialize packet processing context.
+ *
+ * Allowed from states: NOOP
+ *
+ * \param ctx Context.
+ * \param module_param Parameters for given module.
+ * \param module Module API.
+ * \return (module specific state)
+ */
+int knot_process_begin(knot_process_t *ctx, void *module_param, const knot_process_module_t *module);
+
+/*!
+ * \brief Reset current packet processing context.
+ * \param ctx Context.
+ * \return (module specific state)
+ */
+int knot_process_reset(knot_process_t *ctx);
+
+/*!
+ * \brief Finish and close packet processing context.
+ *
+ * Allowed from states: MORE, FULL, DONE, FAIL
+ *
+ * \param ctx Context.
+ * \return (module specific state)
+ */
+int knot_process_finish(knot_process_t *ctx);
+
+/*!
+ * \brief Input more data into packet processing.
+ *
+ * Allowed from states: MORE
+ *
+ * \param wire Source data.
+ * \param wire_len Source data length.
+ * \param ctx Context.
+ * \return (module specific state)
+ */
+int knot_process_in(const uint8_t *wire, uint16_t wire_len, knot_process_t *ctx);
+
+/*!
+ * \brief Write out output from packet processing.
+ *
+ * Allowed from states: FULL, FAIL
+ *
+ * \param wire Destination.
+ * \param wire_len Destination length.
+ * \param ctx Context.
+ * \return (module specific state)
+ */
+int knot_process_out(uint8_t *wire, uint16_t *wire_len, knot_process_t *ctx);
+
+#endif /* _KNOT_PROCESS_H */
+
+/*! @} */
diff --git a/src/libknot/tsig.h b/src/libknot/tsig.h
index 580370080..bd9d364bb 100644
--- a/src/libknot/tsig.h
+++ b/src/libknot/tsig.h
@@ -54,6 +54,19 @@ enum tsig_consts {
 	                          + 6			// time signed
 };
 
+/*! \brief Packet signing context.
+ *  \todo This should be later moved to TSIG files when refactoring. */
+typedef struct knot_sign_context {
+	knot_tsig_key_t *tsig_key;
+	uint8_t *tsig_buf;
+	uint8_t *tsig_digest;
+	size_t tsig_buflen;
+	size_t tsig_digestlen;
+	uint8_t tsig_runlen;
+	uint64_t tsig_time_signed;
+	size_t pkt_count;
+} knot_sign_context_t;
+
 /*!
  * \brief Create TSIG RDATA.
  *
diff --git a/tests/Makefile.inc b/tests/Makefile.inc
index 83a9cbdec..4bf02b55b 100644
--- a/tests/Makefile.inc
+++ b/tests/Makefile.inc
@@ -25,7 +25,7 @@ check_PROGRAMS = \
 	tests/dnssec_zone_nsec \
 	tests/rrset \
 	tests/pkt \
-	tests/ns
+	tests/process_query
 
 check_LIBRARIES = tests/tap/libtap.a
 
diff --git a/tests/TESTS b/tests/TESTS
index dbfec77b0..19f80ce5e 100644
--- a/tests/TESTS
+++ b/tests/TESTS
@@ -22,4 +22,4 @@ dnssec_sign
 dnssec_zone_nsec
 rrset
 pkt
-ns
+process_query
diff --git a/tests/ns.c b/tests/process_query.c
similarity index 87%
rename from tests/ns.c
rename to tests/process_query.c
index 776509455..267719a4c 100644
--- a/tests/ns.c
+++ b/tests/process_query.c
@@ -21,7 +21,7 @@
 #include "common/descriptor.h"
 #include "libknot/packet/wire.h"
 #include "knot/nameserver/name-server.h"
-#include "knot/nameserver/ns_proc_query.h"
+#include "knot/nameserver/process_query.h"
 #include "knot/server/zones.h"
 
 /* SOA RDATA. */
@@ -81,7 +81,7 @@ static void answer_sanity_check(const uint8_t *query,
 }
 
 /* Resolve query and check answer for sanity (2 TAP tests). */
-static void exec_query(ns_proc_context_t *query_ctx, const char *name,
+static void exec_query(knot_process_t *query_ctx, const char *name,
                        const uint8_t *query, uint16_t query_len,
                        uint8_t expected_rcode)
 {
@@ -89,16 +89,16 @@ static void exec_query(ns_proc_context_t *query_ctx, const char *name,
 	uint8_t answer[KNOT_WIRE_MAX_PKTSIZE];
 
 	/* Input packet. */
-	int state = ns_proc_in(query, query_len, query_ctx);
+	int state = knot_process_in(query, query_len, query_ctx);
 
 	ok(state & (NS_PROC_FULL|NS_PROC_FAIL), "ns: process %s query", name);
 
 	/* Create answer. */
-	state = ns_proc_out(answer, &answer_len, query_ctx);
+	state = knot_process_out(answer, &answer_len, query_ctx);
 	if (state & NS_PROC_FAIL) {
 		/* Allow 1 generic error response. */
 		answer_len = KNOT_WIRE_MAX_PKTSIZE;
-		state = ns_proc_out(answer, &answer_len, query_ctx);
+		state = knot_process_out(answer, &answer_len, query_ctx);
 	}
 
 	ok(state == NS_PROC_DONE, "ns: answer %s query", name);
@@ -119,8 +119,8 @@ int main(int argc, char *argv[])
 	plan(8*6 + 3); /* exec_query = 6 TAP tests */
 
 	/* Create processing context. */
-	ns_proc_context_t query_ctx;
-	memset(&query_ctx, 0, sizeof(ns_proc_context_t));
+	knot_process_t query_ctx;
+	memset(&query_ctx, 0, sizeof(knot_process_t));
 	mm_ctx_mempool(&query_ctx.mm, sizeof(knot_pkt_t));
 
 	/* Create name server. */
@@ -130,7 +130,6 @@ int main(int argc, char *argv[])
 	knot_edns_set_payload(ns->opt_rr, 4096);
 	ns->identity = "bogus.ns";
 	ns->version = "0.11";
-	query_ctx.ns = ns;
 
 	/* Insert root zone. */
 	create_root_zone(ns, &query_ctx.mm);
@@ -143,50 +142,51 @@ int main(int argc, char *argv[])
 	knot_pkt_t *query = knot_pkt_new(query_wire, query_len, &query_ctx.mm);
 
 	/* Create query processing parameter. */
-	struct ns_proc_query_param param = {0};
+	struct process_query_param param = {0};
 	sockaddr_set(&param.query_source, AF_INET, "127.0.0.1", 53);
+	param.ns = ns;
 
 	/* Query processor (CH zone) */
-	state = ns_proc_begin(&query_ctx, &param, NS_PROC_QUERY);
+	state = knot_process_begin(&query_ctx, &param, NS_PROC_QUERY);
 	const uint8_t chaos_dname[] = "\2""id""\6""server"; /* id.server */
 	knot_pkt_clear(query);
 	knot_pkt_put_question(query, chaos_dname, KNOT_CLASS_CH, KNOT_RRTYPE_TXT);
 	exec_query(&query_ctx, "CH TXT", query->wire, query->size, KNOT_RCODE_NOERROR);
 
 	/* Query processor (valid input). */
-	state = ns_proc_reset(&query_ctx);
+	state = knot_process_reset(&query_ctx);
 	knot_pkt_clear(query);
 	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
 	exec_query(&query_ctx, "IN/root", query->wire, query->size, KNOT_RCODE_NOERROR);
 
 	/* Query processor (-1 bytes, not enough data). */
-	state = ns_proc_reset(&query_ctx);
+	state = knot_process_reset(&query_ctx);
 	exec_query(&query_ctx, "IN/few-data", query->wire, query->size - 1, KNOT_RCODE_FORMERR);
 
 	/* Query processor (+1 bytes trailing). */
-	state = ns_proc_reset(&query_ctx);
+	state = knot_process_reset(&query_ctx);
 	query->wire[query->size] = '\1'; /* Initialize the "garbage" value. */
 	exec_query(&query_ctx, "IN/trail-garbage", query->wire, query->size + 1, KNOT_RCODE_FORMERR);
 
 	/* Forge NOTIFY query from SOA query. */
-	state = ns_proc_reset(&query_ctx);
+	state = knot_process_reset(&query_ctx);
 	knot_wire_set_opcode(query->wire, KNOT_OPCODE_NOTIFY);
 	exec_query(&query_ctx, "IN/notify", query->wire, query->size, KNOT_RCODE_NOTAUTH);
 
 	/* Forge AXFR query. */
-	ns_proc_reset(&query_ctx);
+	knot_process_reset(&query_ctx);
 	knot_pkt_clear(query);
 	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_AXFR);
 	exec_query(&query_ctx, "IN/axfr", query->wire, query->size, KNOT_RCODE_NOTAUTH);
 
 	/* Forge IXFR query (badly formed, no SOA in AUTHORITY section). */
-	ns_proc_reset(&query_ctx);
+	knot_process_reset(&query_ctx);
 	knot_pkt_clear(query);
 	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_IXFR);
 	exec_query(&query_ctx, "IN/ixfr-formerr", query->wire, query->size, KNOT_RCODE_FORMERR);
 
 	/* Forge IXFR query (well formed). */
-	ns_proc_reset(&query_ctx);
+	knot_process_reset(&query_ctx);
 	/* Append SOA RR. */
 	knot_rrset_t *soa_rr = knot_node_get_rrset(zone->contents->apex, KNOT_RRTYPE_SOA);
 	knot_pkt_begin(query, KNOT_AUTHORITY);
@@ -199,20 +199,20 @@ int main(int argc, char *argv[])
 	/* #189 Process IXFR client. */
 
 	/* Query processor (smaller than DNS header, ignore). */
-	state = ns_proc_reset(&query_ctx);
+	state = knot_process_reset(&query_ctx);
 	knot_pkt_clear(query);
 	knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
-	state = ns_proc_in(query->wire, KNOT_WIRE_HEADER_SIZE - 1, &query_ctx);
+	state = knot_process_in(query->wire, KNOT_WIRE_HEADER_SIZE - 1, &query_ctx);
 	ok(state == NS_PROC_NOOP, "ns: IN/less-than-header query ignored");
 
 	/* Query processor (response, ignore). */
-	state = ns_proc_reset(&query_ctx);
+	state = knot_process_reset(&query_ctx);
 	knot_wire_set_qr(query->wire);
-	state = ns_proc_in(query->wire, query->size, &query_ctx);
+	state = knot_process_in(query->wire, query->size, &query_ctx);
 	ok(state == NS_PROC_NOOP, "ns: IN/less-than-header query ignored");
 
 	/* Finish. */
-	state = ns_proc_finish(&query_ctx);
+	state = knot_process_finish(&query_ctx);
 	ok(state == NS_PROC_NOOP, "ns: processing end" );
 
 	/* Cleanup. */
-- 
GitLab