]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/resolve, modules: NO_ANSWER for not responding to clients
authorOto Šťáva <oto.stava@nic.cz>
Tue, 8 Feb 2022 12:46:15 +0000 (13:46 +0100)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Mon, 28 Feb 2022 10:45:48 +0000 (11:45 +0100)
NEWS
daemon/lua/kres-gen-30.lua
daemon/lua/kres-gen-31.lua
daemon/worker.c
lib/resolve.c
lib/rplan.h
modules/policy/README.rst
modules/policy/policy.lua

diff --git a/NEWS b/NEWS
index d327edf419e48602ad9a874ad8d2f955fbdae254..0d90b3cdf0cfec4811ecaac9453ea19df0e66533 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,7 @@ Improvements
 - validator: conditionally ignore SHA1 DS, as SHOULD by RFC4509 (!1251)
 - lib/resolve: use EDNS padding for outgoing TLS queries (!1254)
 - support for PROXYv2 protocol (!1238)
+- lib/resolve, policy: new NO_ANSWER flag for not responding to clients (!1257)
 
 Incompatible changes
 --------------------
index fd5a14aacc7339180b25a91b0f6a096b2c04b2c9..ca701affd66c6b04fccc955271a35b0816b74a25 100644 (file)
@@ -104,6 +104,7 @@ struct kr_qflags {
        _Bool NO_IPV6 : 1;
        _Bool NO_IPV4 : 1;
        _Bool TCP : 1;
+       _Bool NO_ANSWER : 1;
        _Bool RESOLVED : 1;
        _Bool AWAIT_IPV4 : 1;
        _Bool AWAIT_IPV6 : 1;
index d526b33477907a6e20f1e6d7226326991e68bbd2..0e4fb9a396423c3cbae09883c835022e3e19331a 100644 (file)
@@ -104,6 +104,7 @@ struct kr_qflags {
        _Bool NO_IPV6 : 1;
        _Bool NO_IPV4 : 1;
        _Bool TCP : 1;
+       _Bool NO_ANSWER : 1;
        _Bool RESOLVED : 1;
        _Bool AWAIT_IPV4 : 1;
        _Bool AWAIT_IPV6 : 1;
index baa615e2536b594a0e394d06f2bcefdc9d031708..00782081eeb34d09d72bc82c2a0abeaacd171c08 100644 (file)
@@ -1437,7 +1437,11 @@ static int qr_task_finalize(struct qr_task *task, int state)
                return state == KR_STATE_DONE ? kr_ok() : kr_error(EIO);
        }
 
-       if (unlikely(ctx->req.answer == NULL)) { /* meant to be dropped */
+       /* meant to be dropped */
+       if (unlikely(ctx->req.answer == NULL || ctx->req.options.NO_ANSWER)) {
+               /* For NO_ANSWER, a well-behaved layer should set the state to FAIL */
+               kr_assert(!ctx->req.options.NO_ANSWER || (ctx->req.state & KR_STATE_FAIL));
+
                (void) qr_task_on_send(task, NULL, kr_ok());
                return kr_ok();
        }
index e615749693bc45eebc2d8ac4cf9b4442970cebb0..4559bc0de8447a6157ccc6aa4241c3438b064314 100644 (file)
@@ -732,6 +732,10 @@ knot_rrset_t* kr_request_ensure_edns(struct kr_request *request)
 
 knot_pkt_t *kr_request_ensure_answer(struct kr_request *request)
 {
+       if (request->options.NO_ANSWER) {
+               kr_assert(request->state & KR_STATE_FAIL);
+               return NULL;
+       }
        if (request->answer)
                return request->answer;
 
index a007e9c61752b0c83bfdbd9b130ea60b6efd613a..05594de8f5eebc9edeaffe0f6dac99f36647f72e 100644 (file)
@@ -18,6 +18,9 @@ struct kr_qflags {
        bool NO_IPV6 : 1;        /**< Disable IPv6 */
        bool NO_IPV4 : 1;        /**< Disable IPv4 */
        bool TCP : 1;            /**< Use TCP (or TLS) for this query. */
+       bool NO_ANSWER : 1;      /**< Do not send any answer to the client.
+                                 *   Request state should be set to `KR_STATE_FAIL`
+                                 *   when this flag is set. */
        bool RESOLVED : 1;       /**< Query is resolved.  Note that kr_query gets
                                  *   RESOLVED before following a CNAME chain; see .CNAME. */
        bool AWAIT_IPV4 : 1;     /**< Query is waiting for A address. */
index 1013162562e7eb69c7100ab1aaf09702a64dec98..d8f95d847d2a296b4ee1b6d80e7b6ccdd5327f0a 100644 (file)
@@ -144,6 +144,17 @@ Following actions stop the policy matching on the query, i.e. other rules are no
 
    Terminate query resolution and return REFUSED to the requestor.
 
+.. py:attribute:: NO_ANSWER
+
+   Terminate query resolution and do not return any answer to the requestor.
+
+   .. warning:: During normal operation, an answer should always be returned.
+      Deliberate query drops are indistinguishable from packet loss and may
+      cause problems as described in :rfc:`8906`. Only use :any:`NO_ANSWER`
+      on very specific occasions, e.g. as a defense mechanism during an attack,
+      and prefer other actions (e.g. :any:`DROP` or :any:`REFUSE`) for normal
+      operation.
+
 .. py:attribute:: TC
 
    Force requestor to use TCP. It sets truncated bit (*TC*) in response to true if the request came through UDP, which will force standard-compliant clients to retry the request over TCP.
index f17129bfb20df99cf24001b4b62f5dbe08dfefd8..55a8de3cf13d1592b9f3c2bfe905ea9bb63f357f 100644 (file)
@@ -823,6 +823,12 @@ function policy.DROP(_, req)
        return kres.FAIL
 end
 
+function policy.NO_ANSWER(_, req)
+       req.options.NO_ANSWER = true
+       log_policy_action(req, 'NO_ANSWER')
+       return kres.FAIL
+end
+
 function policy.REFUSE(_, req)
        local answer = answer_clear(req)
        if answer == nil then return nil end