diff --git a/lib/cookies/alg_srvr.c b/lib/cookies/alg_srvr.c
index c3cba6d923bef8d4afb69f85e1fcffc6d8e0c0b1..a3ec3e6499af26faef86e8fb5d1a833583763406 100644
--- a/lib/cookies/alg_srvr.c
+++ b/lib/cookies/alg_srvr.c
@@ -363,12 +363,16 @@ const struct kr_srvr_cookie_alg_descr *kr_srvr_cookie_alg(const struct kr_srvr_c
 	return NULL;
 }
 
-int kr_srvr_cookie_check(const uint8_t *cc, uint16_t cc_len,
-                         const uint8_t *sc, uint16_t sc_len,
+int kr_srvr_cookie_check(const struct kr_dns_cookies *cookies,
                          const struct kr_srvr_cookie_check_ctx *check_ctx,
                          const struct kr_srvr_cookie_alg_descr *sc_alg)
 {
-	if (!cc || !sc || !sc_len || !check_ctx || !sc_alg) {
+	if (!cookies || !check_ctx || !sc_alg) {
+		return kr_error(EINVAL);
+	}
+
+	if (!cookies->cc || !cookies->cc_len ||
+	    !cookies->sc || !cookies->sc_len) {
 		return kr_error(EINVAL);
 	}
 
@@ -382,7 +386,7 @@ int kr_srvr_cookie_check(const uint8_t *cc, uint16_t cc_len,
 		return kr_error(EINVAL);
 	}
 
-	if (sc_len != sc_alg->srvr_cookie_size) {
+	if (cookies->sc_len != sc_alg->srvr_cookie_size) {
 		/* Cookie size does to match. */
 		return kr_error(EBADMSG);
 	}
@@ -390,7 +394,8 @@ int kr_srvr_cookie_check(const uint8_t *cc, uint16_t cc_len,
 	struct kr_srvr_cookie_inbound inbound_sc = { 0, };
 
 	/* Obtain data from received server cookie. */
-	int ret = sc_alg->opt_parse_func(sc, sc_len, &inbound_sc);
+	int ret = sc_alg->opt_parse_func(cookies->sc, cookies->sc_len,
+	                                 &inbound_sc);
 	if (ret != kr_ok()) {
 		return ret;
 	}
@@ -398,8 +403,8 @@ int kr_srvr_cookie_check(const uint8_t *cc, uint16_t cc_len,
 	uint8_t generated_sc[KNOT_OPT_COOKIE_SRVR_MAX] = { 0, };
 	uint16_t generated_sc_len = KNOT_OPT_COOKIE_SRVR_MAX;
 	struct kr_srvr_cookie_input sc_input = {
-		.clnt_cookie = cc,
-		.clnt_cookie_len = cc_len,
+		.clnt_cookie = cookies->cc,
+		.clnt_cookie_len = cookies->cc_len,
 		.nonce = inbound_sc.nonce,
 		.time = inbound_sc.time,
 		.srvr_data = check_ctx
@@ -412,7 +417,7 @@ int kr_srvr_cookie_check(const uint8_t *cc, uint16_t cc_len,
 	}
 	assert(generated_sc_len == sc_alg->srvr_cookie_size);
 
-	ret = (memcmp(sc, generated_sc, generated_sc_len) == 0) ?
+	ret = (memcmp(cookies->sc, generated_sc, generated_sc_len) == 0) ?
 	       kr_ok() : kr_error(EBADMSG);
 
 	return ret;
diff --git a/lib/cookies/alg_srvr.h b/lib/cookies/alg_srvr.h
index e633ace824197f5bc321f9015b55798b3fe5a845..711152acee5a4c634db76b0af5ae2d4cb076cbfa 100644
--- a/lib/cookies/alg_srvr.h
+++ b/lib/cookies/alg_srvr.h
@@ -20,6 +20,14 @@
 
 #include "lib/defines.h"
 
+/** Convenience Structure holding both, server and client, cookies. */
+struct kr_dns_cookies {
+	const uint8_t *cc; /**< Client cookie. */
+	uint16_t cc_len; /**< Client cookie size. */
+	const uint8_t *sc; /**< Server cookie. */
+	uint16_t sc_len; /**< Server cookie size. */
+};
+
 /** Inbound server cookie checking context. */
 struct kr_srvr_cookie_check_ctx {
 	const void *clnt_sockaddr; /**< Client (remote) socket address. */
@@ -91,16 +99,12 @@ const struct kr_srvr_cookie_alg_descr *kr_srvr_cookie_alg(const struct kr_srvr_c
 
 /**
  * @brief Check whether supplied client and server cookie match.
- * @param cc         Client cookie.
- * @param cc_len     Client cookie length.
- * @param sc         Server cookie that should be checked.
- * @param sc_len     Server cookie length.
- * @param check_ctx  Data known to the server needed for cookie validation.
- * @param sc_alg     Server cookie algorithm.
+ * @param cookies   Cookie data.
+ * @param check_ctx Data known to the server needed for cookie validation.
+ * @param sc_alg    Server cookie algorithm.
  * @return kr_ok() if check OK, error code else.
  */
 KR_EXPORT
-int kr_srvr_cookie_check(const uint8_t *cc, uint16_t cc_len,
-                         const uint8_t *sc, uint16_t sc_len,
+int kr_srvr_cookie_check(const struct kr_dns_cookies *cookies,
                          const struct kr_srvr_cookie_check_ctx *check_ctx,
                          const struct kr_srvr_cookie_alg_descr *sc_alg);
diff --git a/lib/cookies/control.c b/lib/cookies/control.c
index c9f47f3356004a0aa2107ab86f13e5e3d123d29c..d6e935d60fc44b02ba8305ced6b89064a4b8b158 100644
--- a/lib/cookies/control.c
+++ b/lib/cookies/control.c
@@ -187,3 +187,79 @@ int kr_request_put_cookie(const struct kr_clnt_cookie_settings *clnt_cntrl,
 	assert(pkt->current == KNOT_ADDITIONAL);
 	return knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, pkt->opt_rr, KNOT_PF_FREE);
 }
+
+int kr_answer_opt_rr_add_cookies(const struct kr_srvr_cookie_input *input,
+                                 const struct kr_srvr_cookie_alg_descr *alg,
+                                 knot_pkt_t *pkt)
+{
+	if (!input || !alg || pkt) {
+		kr_error(EINVAL);
+	}
+
+	uint16_t cookie_size = input->clnt_cookie_len + alg->srvr_cookie_size;
+	uint8_t *data = NULL;
+
+	if (!pkt->opt_rr) {
+		kr_error(EINVAL);
+	}
+	int ret = knot_edns_reserve_option(pkt->opt_rr,
+	                                   KNOT_EDNS_OPTION_COOKIE,
+	                                   cookie_size, &data, &pkt->mm);
+	if (ret != KNOT_EOK) {
+		return kr_error(ret);
+	}
+
+	memcpy(data, input->clnt_cookie, input->clnt_cookie_len);
+	cookie_size = alg->srvr_cookie_size;
+	ret = alg->gen_func(input, data + input->clnt_cookie_len, &cookie_size);
+	if (ret != kr_ok()) {
+		/* TODO -- Delete COOKIE option. */
+		return ret;
+	}
+
+	return ret;
+}
+
+int kr_pkt_set_ext_rcode(knot_pkt_t *pkt, uint16_t whole_rcode)
+{
+	if (!pkt || !knot_pkt_has_edns(pkt)) {
+		return kr_error(EINVAL);
+	}
+
+	uint8_t rcode = whole_rcode & 0x0f;
+	uint8_t ext_rcode = whole_rcode >> 4;
+	knot_wire_set_rcode(pkt->wire, rcode);
+	knot_edns_set_ext_rcode(pkt->opt_rr, ext_rcode);
+
+	return kr_ok();
+}
+
+uint8_t *kr_is_cookie_query(const knot_pkt_t *pkt)
+{
+	if (!pkt || knot_wire_get_qdcount(pkt->wire) > 0) {
+		return false;
+	}
+
+	if (knot_wire_get_qr(pkt->wire) != 0 || !pkt->opt_rr) {
+		return false;
+	}
+
+	return knot_edns_get_option(pkt->opt_rr, KNOT_EDNS_OPTION_COOKIE);
+}
+
+int kr_parse_cookie_opt(uint8_t *cookie_opt, struct kr_dns_cookies *cookies)
+{
+	if (!cookie_opt || !cookies) {
+		kr_error(EINVAL);
+	}
+
+	const uint8_t *cookie_data = knot_edns_opt_get_data(cookie_opt);
+	uint16_t cookie_len = knot_edns_opt_get_length(cookie_opt);
+	assert(cookie_data && cookie_len);
+
+	int ret =  knot_edns_opt_cookie_parse(cookie_data, cookie_len,
+	                                      &cookies->cc, &cookies->cc_len,
+	                                      &cookies->sc, &cookies->sc_len);
+
+	return (ret == KNOT_EOK) ? kr_ok() : kr_error(EINVAL);
+}
diff --git a/lib/cookies/control.h b/lib/cookies/control.h
index a843ce50e70510859f45ddcbd20fe0be2435332f..8d481c042e556a5cc212305e38fc82d4437b1fb7 100644
--- a/lib/cookies/control.h
+++ b/lib/cookies/control.h
@@ -21,6 +21,7 @@
 #include <stdbool.h>
 
 #include "lib/cookies/alg_clnt.h"
+#include "lib/cookies/alg_srvr.h"
 #include "lib/cache.h"
 #include "lib/defines.h"
 
@@ -74,7 +75,7 @@ KR_EXPORT
 extern struct kr_cookie_ctx kr_glob_cookie_ctx;
 
 /**
- * Insert a DNS cookie into query packet.
+ * @brief Insert a DNS cookie into query packet.
  * @note The packet must already contain ENDS section.
  * @param clnt_cntrl    Client cookie control structure.
  * @param cookie_cache  Cookie cache.
@@ -87,3 +88,43 @@ int kr_request_put_cookie(const struct kr_clnt_cookie_settings *clnt_cntrl,
                           struct kr_cache *cookie_cache,
                           const void *clnt_sockaddr, const void *srvr_sockaddr,
                           knot_pkt_t *pkt);
+
+/**
+ * @brief Add cookies into answer.
+ * @note Data are only added into the OPT RR.
+ * @param input input data to generate server cookie from
+ * @param alg algorithm to use
+ * @param pkt packet which to put cookie into
+ * @return kr_ok() or error code
+ */
+KR_EXPORT
+int kr_answer_opt_rr_add_cookies(const struct kr_srvr_cookie_input *input,
+                                 const struct kr_srvr_cookie_alg_descr *alg,
+                                 knot_pkt_t *pkt);
+
+/**
+ * @brief Set RCODE and extended RCODE.
+ * @param pkt DNS packet
+ * @param whole_rcode RCODE value
+ * @return kr_ok() or error code
+ */
+KR_EXPORT
+int kr_pkt_set_ext_rcode(knot_pkt_t *pkt, uint16_t whole_rcode);
+
+/**
+ * @brief Check whether packet is a server cookie request.
+ * @param pkt     Packet to be examined.
+ * @param cookies Received cookies.
+ * @return Pointer to entire cookie option if is a cookie query, NULL else.
+ */
+KR_EXPORT
+uint8_t *kr_is_cookie_query(const knot_pkt_t *pkt);
+
+/**
+ * @brief Parse cookies from cookie option.
+ * @param cookie_opt Cookie option.
+ * @param cookies    Cookie structure to be set.
+ * @return kr_ok() on success, error if cookies are malformed.
+ */
+KR_EXPORT
+int kr_parse_cookie_opt(uint8_t *cookie_opt, struct kr_dns_cookies *cookies);
diff --git a/lib/layer/cookiemonster.c b/lib/layer/cookiemonster.c
index 3878837554b61531f0216f7a730f8ca9a62699d0..b0851bdb97f6c254f09c0a54a7dd3613465c5231 100644
--- a/lib/layer/cookiemonster.c
+++ b/lib/layer/cookiemonster.c
@@ -373,47 +373,13 @@ static int check_response(knot_layer_t *ctx, knot_pkt_t *pkt)
 	return ctx->state;
 }
 
-static int knot_pkt_set_ext_rcode(knot_pkt_t *pkt, uint16_t whole_rcode)
+static inline uint8_t *req_cookie_option(struct kr_request *req)
 {
-	assert(pkt && knot_pkt_has_edns(pkt));
-
-	uint8_t rcode = whole_rcode & 0x0f;
-	uint8_t ext_rcode = whole_rcode >> 4;
-	knot_wire_set_rcode(pkt->wire, rcode);
-	knot_edns_set_ext_rcode(pkt->opt_rr, ext_rcode);
-
-	return kr_ok();
-}
-
-/** Only adds cookies into the OPT RR. */
-static int answer_opt_rr_add_cookies(knot_pkt_t *answer,
-                                     const struct kr_srvr_cookie_input *input,
-                                     const struct kr_srvr_cookie_alg_descr *alg)
-{
-	assert(answer && input && alg);
-
-	size_t cookie_size = input->clnt_cookie_len + alg->srvr_cookie_size;
-	uint8_t *data = NULL;
-
-	if (!answer->opt_rr) {
-		kr_error(EINVAL);
-	}
-	int ret = knot_edns_reserve_option(answer->opt_rr,
-	                                   KNOT_EDNS_OPTION_COOKIE,
-	                                   cookie_size, &data, &answer->mm);
-	if (ret != KNOT_EOK) {
-		return kr_error(ret);
+	if (!req || !req->qsource.opt) {
+		return NULL;
 	}
 
-	memcpy(data, input->clnt_cookie, input->clnt_cookie_len);
-	cookie_size = alg->srvr_cookie_size;
-	ret = alg->gen_func(input, data + input->clnt_cookie_len, &cookie_size);
-	if (ret != kr_ok()) {
-		/* TODO -- Delete COOKIE option. */
-		return ret;
-	}
-
-	return ret;
+	return knot_edns_get_option(req->qsource.opt, KNOT_EDNS_OPTION_COOKIE);
 }
 
 /* TODO -- DNS cookie request. */
@@ -427,75 +393,67 @@ static int check_request(knot_layer_t *ctx, void *module_param)
 	}
 
 	struct kr_request *req = ctx->data;
-	const knot_rrset_t *req_opt_rr = req->qsource.opt;
-
-	if (!req_opt_rr) {
-		return ctx->state;
-	}
-
-	uint8_t *req_cookie_opt = knot_edns_get_option(req_opt_rr,
-	                                               KNOT_EDNS_OPTION_COOKIE);
+	uint8_t *req_cookie_opt = req_cookie_option(req);
 	if (!req_cookie_opt) {
-		return ctx->state;
+		return ctx->state; /* Don't do anything without cookies. */
 	}
 
-	const uint8_t *req_cookie_data = knot_edns_opt_get_data(req_cookie_opt);
-	uint16_t req_cookie_len = knot_edns_opt_get_length(req_cookie_opt);
-	assert(req_cookie_data && req_cookie_len);
-
-	const uint8_t *req_cc = NULL, *req_sc = NULL;
-	uint16_t req_cc_len = 0, req_sc_len = 0;
-	int ret = knot_edns_opt_cookie_parse(req_cookie_data, req_cookie_len,
-	                                     &req_cc, &req_cc_len,
-	                                     &req_sc, &req_sc_len);
-	if (ret != KNOT_EOK) {
-		/* Generate FORMERR response because malformed DNS cookie. */
-		DEBUG_MSG(NULL, "%s\n", "got malformed DNS cookie in request");
+	struct kr_dns_cookies cookies = { 0, };
+	int ret = kr_parse_cookie_opt(req_cookie_opt, &cookies);
+	if (ret != kr_ok()) {
+		/* FORMERR -- malformed cookies. */
+		DEBUG_MSG(NULL, "%s\n", "request with malformed cookie");
 		knot_wire_set_rcode(req->answer->wire, KNOT_RCODE_FORMERR);
 		return KNOT_STATE_FAIL | KNOT_STATE_DONE;
 	}
-	assert(req_cc_len == KNOT_OPT_COOKIE_CLNT);
 
 	bool ignore_badcookie = true; /* TODO -- Occasionally ignore? */
 
-	if (!req_sc) {
-		/* TODO -- Silently discard? */
+	struct kr_srvr_cookie_ctx *srvr_cntrl = &kr_glob_cookie_ctx.srvr;
+	if (!req->qsource.addr ||
+	    !srvr_cntrl->current.ssec || !srvr_cntrl->current.salg) {
+		DEBUG_MSG(NULL, "%s\n", "missing server cookie context");
+		return KNOT_STATE_FAIL;
+	}
+
+	int return_state = ctx->state;
+
+	struct kr_srvr_cookie_check_ctx check_ctx = {
+		.clnt_sockaddr = req->qsource.addr,
+		.secret_data = srvr_cntrl->current.ssec->data,
+		.secret_len = srvr_cntrl->current.ssec->size
+	};
+
+	struct kr_srvr_cookie_input input = {
+		.clnt_cookie = cookies.cc,
+		.clnt_cookie_len = cookies.cc_len,
+		.nonce = kr_rand_uint(UINT32_MAX),
+		.time = req->current_query->timestamp.tv_sec,
+		.srvr_data = &check_ctx
+	};
+
+	if (!cookies.sc) {
+		/* Request has no server cookie. TODO -- Silently discard? */
 		if (!ignore_badcookie) {
 			/* Generate BADCOOKIE response. */
 			DEBUG_MSG(NULL, "%s\n",
-			          "missing server DNS cookie in request");
+			          "request is missing server cookie");
 			if (!knot_pkt_has_edns(req->answer)) {
 				DEBUG_MSG(NULL, "%s\n",
 				          "missing EDNS section in prepared answer");
 				return KNOT_STATE_FAIL;
 			}
-			knot_pkt_set_ext_rcode(req->answer,
-			                       KNOT_RCODE_BADCOOKIE);
-			return KNOT_STATE_FAIL | KNOT_STATE_DONE;
+			kr_pkt_set_ext_rcode(req->answer,
+			                     KNOT_RCODE_BADCOOKIE);
+			return_state = KNOT_STATE_FAIL | KNOT_STATE_DONE;
 		}
-		DEBUG_MSG(NULL, "%s\n",
-		          "ignoring missing server DNS cookie in request");
-		return ctx->state;
+		goto answer_add_cookies;
 	}
 
 	/* Check server cookie obtained in request. */
 
-	struct kr_srvr_cookie_ctx *srvr_cntrl = &kr_glob_cookie_ctx.srvr;
-
-	if (!req->qsource.addr ||
-	    !srvr_cntrl->current.ssec || !srvr_cntrl->current.salg) {
-		DEBUG_MSG(NULL, "%s\n", "no server DNS cookie context data");
-		return KNOT_STATE_FAIL;
-	}
-
-	struct kr_srvr_cookie_check_ctx check_ctx = {
-		.clnt_sockaddr = req->qsource.addr,
-		.secret_data = srvr_cntrl->current.ssec->data,
-		.secret_len = srvr_cntrl->current.ssec->size
-	};
-
-	ret = kr_srvr_cookie_check(req_cc, req_cc_len, req_sc, req_sc_len,
-	                           &check_ctx, srvr_cntrl->current.salg);
+	ret = kr_srvr_cookie_check(&cookies, &check_ctx,
+	                           srvr_cntrl->current.salg);
 	if (ret == kr_error(EBADMSG) &&
 	    srvr_cntrl->recent.ssec && srvr_cntrl->recent.salg) {
 		/* Try recent algorithm. */
@@ -504,8 +462,7 @@ static int check_request(knot_layer_t *ctx, void *module_param)
 			.secret_data = srvr_cntrl->recent.ssec->data,
 			.secret_len = srvr_cntrl->recent.ssec->size
 		};
-		ret = kr_srvr_cookie_check(req_cc, req_cc_len, req_sc,
-		                           req_sc_len, &recent_ctx,
+		ret = kr_srvr_cookie_check(&cookies, &recent_ctx,
 		                           srvr_cntrl->recent.salg);
 	}
 	if (ret != kr_ok()) {
@@ -513,34 +470,26 @@ static int check_request(knot_layer_t *ctx, void *module_param)
 		if (!ignore_badcookie) {
 			/* Generate BADCOOKIE response. */
 			DEBUG_MSG(NULL, "%s\n",
-			          "invalid server DNS cookie in request");
+			          "request has invalid server cookie");
 			if (!knot_pkt_has_edns(req->answer)) {
 				DEBUG_MSG(NULL, "%s\n",
 				          "missing EDNS section in prepared answer");
 				return KNOT_STATE_FAIL;
 			}
-			knot_pkt_set_ext_rcode(req->answer,
-			                       KNOT_RCODE_BADCOOKIE);
-			return KNOT_STATE_FAIL | KNOT_STATE_DONE;
+			kr_pkt_set_ext_rcode(req->answer,
+			                     KNOT_RCODE_BADCOOKIE);
+			return_state = KNOT_STATE_FAIL | KNOT_STATE_DONE;
 		}
-		DEBUG_MSG(NULL, "%s\n",
-		          "ignoring invalid server DNS cookie in request");
-		return ctx->state;
+		goto answer_add_cookies;
 	}
 
-	/* Server cookie OK. */
+	/* Server cookie is OK. */
 
+answer_add_cookies:
 	/* Add server cookie into response. */
-	struct kr_srvr_cookie_input input = {
-		.clnt_cookie = req_cc,
-		.clnt_cookie_len = req_cc_len,
-		.nonce = 0, /*TODO -- Some pseudo-random value? */
-		.time = req->current_query->timestamp.tv_sec,
-		.srvr_data = &check_ctx
-	};
-	answer_opt_rr_add_cookies(req->answer, &input, srvr_cntrl->current.salg);
-
-	return ctx->state;
+	kr_answer_opt_rr_add_cookies(&input, srvr_cntrl->current.salg,
+	                             req->answer);
+	return return_state;
 }
 
 /** Module implementation. */
diff --git a/lib/resolve.c b/lib/resolve.c
index 076b08a116acac5e297d79e7a9bb1e45cd5bc977..0266de469fe7622fb5b21b78928df5bf0be375bf 100644
--- a/lib/resolve.c
+++ b/lib/resolve.c
@@ -389,6 +389,67 @@ int kr_resolve_begin(struct kr_request *request, struct kr_context *ctx, knot_pk
 	return KNOT_STATE_CONSUME;
 }
 
+#if defined(ENABLE_COOKIES)
+/**
+ * @brief Put cookie into answer packet.
+ * @param clnt_sockaddr client socket address
+ * @param srvr_cntrl control structure of the server cookie algorithm
+ * @param cookies obtained cookies
+ * @param answer answer packet
+ * @return state
+ */
+static int cookie_answer(const void *clnt_sockaddr,
+                         const struct kr_srvr_cookie_ctx *srvr_cntrl,
+                         struct kr_dns_cookies *cookies, knot_pkt_t *answer)
+{
+	assert(srvr_cntrl && cookies && answer);
+
+	/* Initialise answer. */
+	knot_wire_set_qr(answer->wire);
+	knot_wire_clear_aa(answer->wire);
+	knot_wire_set_ra(answer->wire);
+	knot_wire_set_rcode(answer->wire, KNOT_RCODE_NOERROR);
+
+	struct kr_srvr_cookie_check_ctx check_ctx = {
+		.clnt_sockaddr = clnt_sockaddr,
+		.secret_data = srvr_cntrl->current.ssec->data,
+		.secret_len = srvr_cntrl->current.ssec->size
+	};
+
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+
+	struct kr_srvr_cookie_input input = {
+		.clnt_cookie = cookies->cc,
+		.clnt_cookie_len = cookies->cc_len,
+		.nonce = kr_rand_uint(UINT32_MAX),
+		.time = tv.tv_sec,
+		.srvr_data = &check_ctx
+	};
+
+	int ret =  kr_answer_opt_rr_add_cookies(&input,
+	                                        srvr_cntrl->current.salg,
+	                                        answer);
+	if (ret != kr_ok()) {
+		return KNOT_STATE_FAIL;
+	}
+
+	if (!cookies->sc) {
+		return KNOT_STATE_DONE;
+	}
+
+	/* Check server cookie only with current settings. */
+	ret = kr_srvr_cookie_check(cookies, &check_ctx,
+	                           srvr_cntrl->current.salg);
+	if (ret != kr_ok()) {
+		kr_pkt_set_ext_rcode(answer, KNOT_RCODE_BADCOOKIE);
+		return KNOT_STATE_FAIL | KNOT_STATE_DONE;
+	}
+
+	return KNOT_STATE_DONE;
+}
+#endif /* defined(ENABLE_COOKIES) */
+
 static int resolve_query(struct kr_request *request, const knot_pkt_t *packet)
 {
 	struct kr_rplan *rplan = &request->rplan;
@@ -397,7 +458,24 @@ static int resolve_query(struct kr_request *request, const knot_pkt_t *packet)
 	uint16_t qtype = knot_pkt_qtype(packet);
 	struct kr_query *qry = kr_rplan_push(rplan, NULL, qname, qclass, qtype);
 	if (!qry) {
+#if defined(ENABLE_COOKIES)
+		/* May be a DNS cookies query. */
+		struct kr_dns_cookies cookies = { 0, };
+		uint8_t *cookie_opt = kr_is_cookie_query(packet);
+		if (cookie_opt && kr_glob_cookie_ctx.clnt.enabled) {
+			if (kr_ok() != kr_parse_cookie_opt(cookie_opt,
+			                                   &cookies)) {
+				/* TODO -- KNOT_RCODE_FORMERR? */
+				return KNOT_STATE_FAIL;
+			}
+		}
+
+		return cookie_answer(request->qsource.addr,
+		                     &kr_glob_cookie_ctx.srvr,
+		                     &cookies, request->answer);
+#else /* !defined(ENABLE_COOKIES) */
 		return KNOT_STATE_FAIL;
+#endif /* defined(ENABLE_COOKIES) */
 	}
 
 	/* Deferred zone cut lookup for this query. */