]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add a quota for SIG(0) signature checks
authorAram Sargsyan <aram@isc.org>
Mon, 25 Mar 2024 11:07:47 +0000 (11:07 +0000)
committerNicki Křížek <nicki@isc.org>
Mon, 10 Jun 2024 15:33:08 +0000 (17:33 +0200)
In order to protect from a malicious DNS client that sends many
queries with a SIG(0)-signed message, add a quota of simultaneously
running SIG(0) checks.

This protection can only help when named is using more than one worker
threads. For example, if named is running with the '-n 4' option, and
'sig0checks-quota 2;' is used, then named will make sure to not use
more than 2 workers for the SIG(0) signature checks in parallel, thus
leaving the other workers to serve the remaining clients which do not
use SIG(0)-signed messages.

That limitation is going to change when SIG(0) signature checks are
offloaded to "slow" threads in a future commit.

The 'sig0checks-quota-exempt' ACL option can be used to exempt certain
clients from the quota requirements using their IP or network addresses.

The 'sig0checks-quota-maxwait-ms' option is used to define a maximum
amount of time for named to wait for a quota to appear. If during that
time no new quota becomes available, named will answer to the client
with DNS_R_REFUSED.

13 files changed:
bin/delv/delv.c
bin/named/config.c
bin/named/server.c
bin/tests/system/checkconf/bad-sig0checks-quota-exempt.conf [new file with mode: 0644]
bin/tests/system/checkconf/good-sig0checks-quota-exempt.conf [new file with mode: 0644]
doc/misc/options
lib/isccfg/check.c
lib/isccfg/namedconf.c
lib/ns/client.c
lib/ns/include/ns/client.h
lib/ns/include/ns/server.h
lib/ns/server.c
tests/libtest/ns.c

index e06322de11d650653533619af3e05592a63a9629..5af7c15653b8320d0d538f748bc613be9e0ee0fb 100644 (file)
@@ -2117,12 +2117,13 @@ cleanup:
 
 static isc_result_t
 matchview(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
-         dns_message_t *message, dns_aclenv_t *env, isc_result_t *sigresultp,
-         dns_view_t **viewp) {
+         dns_message_t *message, dns_aclenv_t *env, ns_server_t *lsctx,
+         isc_result_t *sigresultp, dns_view_t **viewp) {
        UNUSED(srcaddr);
        UNUSED(destaddr);
        UNUSED(message);
        UNUSED(env);
+       UNUSED(lsctx);
        UNUSED(sigresultp);
 
        *viewp = view;
index 732e28e606a9e3541eea3677e458aea585e12a42..62c05a8314180e8d792afe4d029f8e25a0da6def 100644 (file)
@@ -109,6 +109,8 @@ options {\n\
 #      session-keyfile \"" NAMED_LOCALSTATEDIR "/run/named/session.key\";\n\
        session-keyname local-ddns;\n\
        startup-notify-rate 20;\n\
+       sig0checks-quota 1;\n\
+       sig0checks-quota-maxwait-ms 1500;\n\
        statistics-file \"named.stats\";\n\
        tcp-advertised-timeout 300;\n\
        tcp-clients 150;\n\
index c41f5d9b249c21c997558316766c3b5148c50479..162b556542488510909caa5cd5930e2456c9d18b 100644 (file)
@@ -8412,6 +8412,8 @@ load_configuration(const char *filename, named_server_t *server,
        configure_server_quota(maps, "recursive-clients",
                               &server->sctx->recursionquota);
        configure_server_quota(maps, "update-quota", &server->sctx->updquota);
+       configure_server_quota(maps, "sig0checks-quota",
+                              &server->sctx->sig0checksquota);
 
        max = isc_quota_getmax(&server->sctx->recursionquota);
        if (max > 1000) {
@@ -8430,9 +8432,23 @@ load_configuration(const char *filename, named_server_t *server,
        } else {
                softquota = (max * 90) / 100;
        }
-
        isc_quota_soft(&server->sctx->recursionquota, softquota);
 
+       obj = NULL;
+       result = named_config_get(maps, "sig0checks-quota-exempt", &obj);
+       if (result == ISC_R_SUCCESS) {
+               result = cfg_acl_fromconfig(
+                       obj, config, named_g_lctx, named_g_aclconfctx,
+                       named_g_mctx, 0, &server->sctx->sig0checksquota_exempt);
+               INSIST(result == ISC_R_SUCCESS);
+       }
+
+       obj = NULL;
+       result = named_config_get(maps, "sig0checks-quota-maxwait-ms", &obj);
+       if (result == ISC_R_SUCCESS) {
+               server->sctx->sig0checksquota_maxwaitms = cfg_obj_asuint32(obj);
+       }
+
        /*
         * Set "blackhole". Only legal at options level; there is
         * no default.
@@ -10048,11 +10064,12 @@ shutdown_server(void *arg) {
  */
 static isc_result_t
 get_matching_view(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
-                 dns_message_t *message, dns_aclenv_t *env,
+                 dns_message_t *message, dns_aclenv_t *env, ns_server_t *sctx,
                  isc_result_t *sigresult, dns_view_t **viewp) {
        dns_view_t *view;
 
        REQUIRE(message != NULL);
+       REQUIRE(sctx != NULL);
        REQUIRE(sigresult != NULL);
        REQUIRE(viewp != NULL && *viewp == NULL);
 
@@ -10063,13 +10080,49 @@ get_matching_view(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
                    message->rdclass == dns_rdataclass_any)
                {
                        const dns_name_t *tsig = NULL;
+                       int exempt_match;
+                       isc_result_t sig0_qresult = ISC_R_UNSET;
+
+                       if (message->sig0 != NULL) {
+                               /*
+                                * If the message has a SIG0 signature and the
+                                * client is not exempt from the quota, then
+                                * acquire a quota. If quota is reached, then
+                                * return early.
+                                */
+                               if (sctx->sig0checksquota_exempt != NULL) {
+                                       isc_result_t result = dns_acl_match(
+                                               srcaddr, NULL,
+                                               sctx->sig0checksquota_exempt,
+                                               env, &exempt_match, NULL);
+                                       if (result == ISC_R_SUCCESS &&
+                                           exempt_match > 0)
+                                       {
+                                               sig0_qresult = ISC_R_EXISTS;
+                                       }
+                               }
+                               if (sig0_qresult == ISC_R_UNSET) {
+                                       sig0_qresult = isc_quota_acquire(
+                                               &sctx->sig0checksquota);
+                               }
+                               if (sig0_qresult == ISC_R_SOFTQUOTA) {
+                                       isc_quota_release(
+                                               &sctx->sig0checksquota);
+                               }
+                               if (sig0_qresult != ISC_R_SUCCESS &&
+                                   sig0_qresult != ISC_R_EXISTS)
+                               {
+                                       return (ISC_R_QUOTA);
+                               }
+                       }
 
+                       /* Check the signature, then release the quota  */
                        *sigresult = dns_message_rechecksig(message, view);
+                       if (sig0_qresult == ISC_R_SUCCESS) {
+                               isc_quota_release(&sctx->sig0checksquota);
+                       }
                        if (*sigresult == ISC_R_SUCCESS) {
-                               dns_tsigkey_t *tsigkey;
-
-                               tsigkey = message->tsigkey;
-                               tsig = dns_tsigkey_identity(tsigkey);
+                               tsig = dns_tsigkey_identity(message->tsigkey);
                        }
 
                        if (dns_acl_allowed(srcaddr, tsig, view->matchclients,
diff --git a/bin/tests/system/checkconf/bad-sig0checks-quota-exempt.conf b/bin/tests/system/checkconf/bad-sig0checks-quota-exempt.conf
new file mode 100644 (file)
index 0000000..c54227d
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0.  If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+options {
+       sig0checks-quota-exempt { unknownacl; };
+};
diff --git a/bin/tests/system/checkconf/good-sig0checks-quota-exempt.conf b/bin/tests/system/checkconf/good-sig0checks-quota-exempt.conf
new file mode 100644 (file)
index 0000000..2968ebe
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0.  If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+acl goodacl {
+       192.168.0.1;
+};
+
+options {
+       sig0checks-quota-exempt { 10.0.0.0/8; 2001:db8::100; goodacl; };
+};
index de24eef2c216d7cd792b6b4d31d73d8ad2e3ef27..d003e9e74104824b6361a278d05ecb9a0b4b42cf 100644 (file)
@@ -277,6 +277,9 @@ options {
        sig-signing-signatures <integer>;
        sig-signing-type <integer>;
        sig-validity-interval <integer> [ <integer> ]; // obsolete
+       sig0checks-quota <integer>;
+       sig0checks-quota-exempt { <address_match_element>; ... };
+       sig0checks-quota-maxwait-ms <integer>;
        sortlist { <address_match_element>; ... }; // deprecated
        stale-answer-client-timeout ( disabled | off | <integer> );
        stale-answer-enable <boolean>;
index e0c4bdc5b83c5b22079db2a9e22ed59d7444d6e2..8dd8efa934605ca3f33792310e366ff23b9c75d1 100644 (file)
@@ -2073,6 +2073,21 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config,
 
        cfg_aclconfctx_create(mctx, &actx);
 
+       obj = NULL;
+       (void)cfg_map_get(options, "sig0checks-quota-exempt", &obj);
+       if (obj != NULL) {
+               dns_acl_t *acl = NULL;
+
+               tresult = cfg_acl_fromconfig(obj, config, logctx, actx, mctx, 0,
+                                            &acl);
+               if (acl != NULL) {
+                       dns_acl_detach(&acl);
+               }
+               if (result == ISC_R_SUCCESS) {
+                       result = tresult;
+               }
+       }
+
        obj = NULL;
        (void)cfg_map_get(options, "listen-on", &obj);
        if (obj != NULL) {
index 18b40fab7ffb131f408ac1c1cef9589f294155d4..3a27432f7ec48a7bf05f3861624bacb10cbb5ffc 100644 (file)
@@ -1361,6 +1361,9 @@ static cfg_clausedef_t options_clauses[] = {
        { "session-keyalg", &cfg_type_astring, 0 },
        { "session-keyfile", &cfg_type_qstringornone, 0 },
        { "session-keyname", &cfg_type_astring, 0 },
+       { "sig0checks-quota", &cfg_type_uint32, 0 },
+       { "sig0checks-quota-maxwait-ms", &cfg_type_uint32, 0 },
+       { "sig0checks-quota-exempt", &cfg_type_bracketed_aml, 0 },
        { "sit-secret", NULL, CFG_CLAUSEFLAG_ANCIENT },
        { "stacksize", &cfg_type_size, CFG_CLAUSEFLAG_ANCIENT },
        { "startup-notify-rate", &cfg_type_uint32, 0 },
index c1341be828f3b36015ddd41168dc6ee37b6d7ba9..03afc4d25c043f5f9cdcb2a07e95e68a767181f8 100644 (file)
 
 atomic_uint_fast64_t ns_client_requests = 0;
 
+static atomic_uint_fast32_t last_sigchecks_quota_log = 0;
+
+static bool
+can_log_sigchecks_quota(void) {
+       isc_stdtime_t last;
+       isc_stdtime_t now = isc_stdtime_now();
+       last = atomic_exchange_relaxed(&last_sigchecks_quota_log, now);
+       if (now != last) {
+               return (true);
+       }
+
+       return (false);
+}
+
 static void
 clientmgr_destroy_cb(void *arg);
 static void
 ns_client_dumpmessage(ns_client_t *client, const char *reason);
 static void
+ns_client_request_continue(void *arg);
+static void
 compute_cookie(ns_client_t *client, uint32_t when, const unsigned char *secret,
               isc_buffer_t *buf);
 
@@ -1739,6 +1755,53 @@ ns__client_put_cb(void *client0) {
        ns_clientmgr_detach(&manager);
 }
 
+static isc_result_t
+ns_client_setup_view(ns_client_t *client, isc_netaddr_t *netaddr) {
+       isc_result_t result;
+
+       result = client->manager->sctx->matchingview(
+               netaddr, &client->destaddr, client->message,
+               client->manager->aclenv, client->manager->sctx,
+               &client->sigresult, &client->view);
+       if (result != ISC_R_SUCCESS) {
+               if (result == ISC_R_QUOTA) {
+                       if (can_log_sigchecks_quota()) {
+                               ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+                                             NS_LOGMODULE_CLIENT, ISC_LOG_INFO,
+                                             "SIG(0) checks quota reached");
+                               ns_client_dumpmessage(
+                                       client, "SIG(0) checks quota reached");
+                       }
+               } else {
+                       char classname[DNS_RDATACLASS_FORMATSIZE];
+                       isc_buffer_t b;
+                       isc_region_t *r;
+
+                       /*
+                        * Do a dummy TSIG verification attempt so that the
+                        * response will have a TSIG if the query did, as
+                        * required by RFC2845.
+                        */
+                       dns_message_resetsig(client->message);
+                       r = dns_message_getrawmessage(client->message);
+                       isc_buffer_init(&b, r->base, r->length);
+                       isc_buffer_add(&b, r->length);
+                       (void)dns_tsig_verify(&b, client->message, NULL, NULL);
+
+                       dns_rdataclass_format(client->message->rdclass,
+                                             classname, sizeof(classname));
+                       ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+                                     NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
+                                     "no matching view in class '%s'",
+                                     classname);
+                       ns_client_dumpmessage(client,
+                                             "no matching view in class");
+               }
+       }
+
+       return (result);
+}
+
 /*
  * Handle an incoming request event from the socket (UDP case)
  * or tcpmsg (TCP case).
@@ -1748,12 +1811,7 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
                  isc_region_t *region, void *arg) {
        ns_client_t *client = NULL;
        isc_result_t result;
-       isc_result_t sigresult = ISC_R_SUCCESS;
-       isc_buffer_t *buffer = NULL;
-       isc_buffer_t tbuffer;
        dns_rdataset_t *opt = NULL;
-       const dns_name_t *signame = NULL;
-       bool ra; /* Recursion available. */
        isc_netaddr_t netaddr;
        int match;
        dns_messageid_t id;
@@ -1761,28 +1819,6 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
        bool notimp;
        size_t reqsize;
        dns_aclenv_t *env = NULL;
-#ifdef HAVE_DNSTAP
-       dns_transport_type_t transport_type;
-       dns_dtmsgtype_t dtmsgtype;
-#endif /* ifdef HAVE_DNSTAP */
-       static const char *ra_reasons[] = {
-               "ACLs not processed yet",
-               "no resolver in view",
-               "recursion not enabled for view",
-               "allow-recursion did not match",
-               "allow-query-cache did not match",
-               "allow-recursion-on did not match",
-               "allow-query-cache-on did not match",
-       };
-       enum refusal_reasons {
-               INVALID,
-               NO_RESOLVER,
-               RECURSION_DISABLED,
-               ALLOW_RECURSION,
-               ALLOW_QUERY_CACHE,
-               ALLOW_RECURSION_ON,
-               ALLOW_QUERY_CACHE_ON
-       } ra_refusal_reason = INVALID;
 
        if (eresult != ISC_R_SUCCESS) {
                return;
@@ -1830,14 +1866,14 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
 
        (void)atomic_fetch_add_relaxed(&ns_client_requests, 1);
 
-       isc_buffer_init(&tbuffer, region->base, region->length);
-       isc_buffer_add(&tbuffer, region->length);
-       buffer = &tbuffer;
+       isc_buffer_init(&client->tbuffer, region->base, region->length);
+       isc_buffer_add(&client->tbuffer, region->length);
+       client->buffer = &client->tbuffer;
 
        client->peeraddr = isc_nmhandle_peeraddr(handle);
        client->peeraddr_valid = true;
 
-       reqsize = isc_buffer_usedlength(buffer);
+       reqsize = isc_buffer_usedlength(client->buffer);
 
        client->state = NS_CLIENTSTATE_WORKING;
 
@@ -1876,7 +1912,7 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
                      ISC_LOG_DEBUG(3), "%s request",
                      TCP_CLIENT(client) ? "TCP" : "UDP");
 
-       result = dns_message_peekheader(buffer, &id, &flags);
+       result = dns_message_peekheader(client->buffer, &id, &flags);
        if (result != ISC_R_SUCCESS) {
                /*
                 * There isn't enough header to determine whether
@@ -1951,7 +1987,7 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
        /*
         * It's a request.  Parse it.
         */
-       result = dns_message_parse(client->message, buffer, 0);
+       result = dns_message_parse(client->message, client->buffer, 0);
        if (result != ISC_R_SUCCESS) {
                /*
                 * Parsing the request failed.  Send a response
@@ -2080,38 +2116,119 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
        client->destsockaddr = isc_nmhandle_localaddr(handle);
        isc_netaddr_fromsockaddr(&client->destaddr, &client->destsockaddr);
 
-       result = client->manager->sctx->matchingview(
-               &netaddr, &client->destaddr, client->message, env, &sigresult,
-               &client->view);
-       if (result != ISC_R_SUCCESS) {
-               char classname[DNS_RDATACLASS_FORMATSIZE];
-
-               /*
-                * Do a dummy TSIG verification attempt so that the
-                * response will have a TSIG if the query did, as
-                * required by RFC2845.
-                */
-               isc_buffer_t b;
-               isc_region_t *r;
-
-               dns_message_resetsig(client->message);
-
-               r = dns_message_getrawmessage(client->message);
-               isc_buffer_init(&b, r->base, r->length);
-               isc_buffer_add(&b, r->length);
-               (void)dns_tsig_verify(&b, client->message, NULL, NULL);
-
-               dns_rdataclass_format(client->message->rdclass, classname,
-                                     sizeof(classname));
+       result = ns_client_setup_view(client, &netaddr);
+       if (result == ISC_R_QUOTA) {
                ns_client_log(client, NS_LOGCATEGORY_CLIENT,
-                             NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
-                             "no matching view in class '%s'", classname);
-               ns_client_dumpmessage(client, "no matching view in class");
+                             NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(5),
+                             "client is starting to wait for quota");
+               client->async = true;
+               isc_nmhandle_ref(client->handle);
+               isc_async_run(client->manager->loop, ns_client_request_continue,
+                             client);
+               return;
+       } else if (result != ISC_R_SUCCESS) {
                ns_client_extendederror(client, DNS_EDE_PROHIBITED, NULL);
                ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_REFUSED);
                return;
        }
 
+       ns_client_request_continue(client);
+}
+
+static void
+ns_client_request_continue(void *arg) {
+       ns_client_t *client = arg;
+       isc_netaddr_t netaddr;
+       const dns_name_t *signame = NULL;
+       bool ra; /* Recursion available. */
+       isc_result_t result;
+       static const char *ra_reasons[] = {
+               "ACLs not processed yet",
+               "no resolver in view",
+               "recursion not enabled for view",
+               "allow-recursion did not match",
+               "allow-query-cache did not match",
+               "allow-recursion-on did not match",
+               "allow-query-cache-on did not match",
+       };
+       enum refusal_reasons {
+               INVALID,
+               NO_RESOLVER,
+               RECURSION_DISABLED,
+               ALLOW_RECURSION,
+               ALLOW_QUERY_CACHE,
+               ALLOW_RECURSION_ON,
+               ALLOW_QUERY_CACHE_ON
+       } ra_refusal_reason = INVALID;
+#ifdef HAVE_DNSTAP
+       dns_transport_type_t transport_type;
+       dns_dtmsgtype_t dtmsgtype;
+#endif /* ifdef HAVE_DNSTAP */
+
+       /*
+        * This function could be running asynchronously if a quota was reached
+        * before, and named was waiting for available quota. In that case we
+        * need to update the current 'now', and check that named doesn't wait
+        * for too long.
+        */
+       if (client->async) {
+               uint64_t wait_us;
+               uint64_t maxwait_us;
+
+               client->tnow = isc_time_now();
+               client->now = isc_time_seconds(&client->tnow);
+
+               wait_us = isc_time_microdiff(&client->tnow,
+                                            &client->requesttime);
+               maxwait_us = US_PER_MS *
+                            client->manager->sctx->sig0checksquota_maxwaitms;
+               if (wait_us > maxwait_us) {
+                       isc_buffer_t b;
+                       isc_region_t *r;
+
+                       ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+                                     NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(5),
+                                     "client reached max wait time for quota");
+
+                       /*
+                        * Do a dummy TSIG verification attempt so that the
+                        * response will have a TSIG if the query did, as
+                        * required by RFC2845.
+                        */
+                       dns_message_resetsig(client->message);
+                       r = dns_message_getrawmessage(client->message);
+                       isc_buffer_init(&b, r->base, r->length);
+                       isc_buffer_add(&b, r->length);
+                       (void)dns_tsig_verify(&b, client->message, NULL, NULL);
+
+                       ns_client_extendederror(client, DNS_EDE_PROHIBITED,
+                                               NULL);
+                       ns_client_error(client, DNS_R_REFUSED);
+                       goto cleanup;
+               }
+       }
+
+       isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+
+       if (client->view == NULL) {
+               result = ns_client_setup_view(client, &netaddr);
+               if (result == ISC_R_QUOTA) {
+                       ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+                                     NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(5),
+                                     "client continues waiting for quota");
+                       client->async = true;
+                       isc_nmhandle_ref(client->handle);
+                       isc_async_run(client->manager->loop,
+                                     ns_client_request_continue, client);
+                       goto cleanup;
+               } else if (result != ISC_R_SUCCESS) {
+                       ns_client_extendederror(client, DNS_EDE_PROHIBITED,
+                                               NULL);
+                       ns_client_error(client, DNS_R_REFUSED);
+                       goto cleanup;
+               }
+       }
+
        if (isc_nm_is_proxy_handle(client->handle)) {
                char fmtbuf[ISC_SOCKADDR_FORMATSIZE] = { 0 };
                isc_netaddr_t real_local_addr, real_peer_addr;
@@ -2140,8 +2257,8 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
                                        "ACL",
                                        fmtbuf);
                        }
-                       isc_nm_bad_request(handle);
-                       return;
+                       isc_nm_bad_request(client->handle);
+                       goto cleanup;
                }
 
                /* allow by default */
@@ -2161,8 +2278,8 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
                                        "'allow-proxy-on' ACL",
                                        fmtbuf);
                        }
-                       isc_nm_bad_request(handle);
-                       return;
+                       isc_nm_bad_request(client->handle);
+                       goto cleanup;
                }
        }
 
@@ -2255,8 +2372,8 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
                if (!(client->message->tsigstatus == dns_tsigerror_badkey &&
                      client->message->opcode == dns_opcode_update))
                {
-                       ns_client_error(client, sigresult);
-                       return;
+                       ns_client_error(client, client->sigresult);
+                       goto cleanup;
                }
        }
 
@@ -2340,25 +2457,25 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
 
                dns_dt_send(client->view, dtmsgtype, &client->peeraddr,
                            &client->destsockaddr, transport_type, NULL,
-                           &client->requesttime, NULL, buffer);
+                           &client->requesttime, NULL, client->buffer);
 #endif /* HAVE_DNSTAP */
 
-               ns_query_start(client, handle);
+               ns_query_start(client, client->handle);
                break;
        case dns_opcode_update:
                CTRACE("update");
 #ifdef HAVE_DNSTAP
                dns_dt_send(client->view, DNS_DTTYPE_UQ, &client->peeraddr,
                            &client->destsockaddr, transport_type, NULL,
-                           &client->requesttime, NULL, buffer);
+                           &client->requesttime, NULL, client->buffer);
 #endif /* HAVE_DNSTAP */
                ns_client_settimeout(client, 60);
-               ns_update_start(client, handle, sigresult);
+               ns_update_start(client, client->handle, client->sigresult);
                break;
        case dns_opcode_notify:
                CTRACE("notify");
                ns_client_settimeout(client, 60);
-               ns_notify_start(client, handle);
+               ns_notify_start(client, client->handle);
                break;
        case dns_opcode_iquery:
                CTRACE("iquery");
@@ -2368,6 +2485,15 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
                CTRACE("unknown opcode");
                ns_client_error(client, DNS_R_NOTIMP);
        }
+
+cleanup:
+       if (client->async) {
+               /*
+                * Do not detach, only 'unref' the corresponding 'ref' when
+                * async was used, because the client can still be reused.
+                */
+               isc_nmhandle_unref(client->handle);
+       }
 }
 
 isc_result_t
index 67126b01f7dc5444671d4cd2c4431b86eb48491f..39c1fa0a86431abf9469eecabf6d26173c0bf12c 100644 (file)
@@ -167,6 +167,7 @@ struct ns_client {
        ns_clientmgr_t  *manager;
        ns_clientstate_t state;
        bool             nodetach;
+       bool             async;
        unsigned int     attributes;
        dns_view_t      *view;
        dns_dispatch_t  *dispatch;
@@ -192,6 +193,9 @@ struct ns_client {
        isc_time_t    tnow;
        dns_name_t    signername; /*%< [T]SIG key name */
        dns_name_t   *signer;     /*%< NULL if not valid sig */
+       isc_result_t  sigresult;
+       isc_buffer_t *buffer;
+       isc_buffer_t  tbuffer;
 
        isc_sockaddr_t peeraddr;
        bool           peeraddr_valid;
index 84003bf57f924372ddf44d7d34d56ccbd0feb50e..fae2040b74e6f954aa400628eaa730d0d7d4d880 100644 (file)
@@ -64,9 +64,12 @@ typedef void (*ns_fuzzcb_t)(void);
 /*%
  * Type for callback function to get the view that can answer a query.
  */
-typedef isc_result_t (*ns_matchview_t)(
-       isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr, dns_message_t *message,
-       dns_aclenv_t *env, isc_result_t *sigresultp, dns_view_t **viewp);
+typedef isc_result_t (*ns_matchview_t)(isc_netaddr_t *srcaddr,
+                                      isc_netaddr_t *destaddr,
+                                      dns_message_t *message,
+                                      dns_aclenv_t *env, ns_server_t *sctx,
+                                      isc_result_t *sigresultp,
+                                      dns_view_t  **viewp);
 
 /*%
  * Server context.
@@ -88,6 +91,9 @@ struct ns_server {
        isc_quota_t tcpquota;
        isc_quota_t xfroutquota;
        isc_quota_t updquota;
+       isc_quota_t sig0checksquota;
+       uint32_t    sig0checksquota_maxwaitms;
+       dns_acl_t  *sig0checksquota_exempt;
        ISC_LIST(isc_quota_t) http_quotas;
        isc_mutex_t http_quotas_lock;
 
index d853848a7639be2d580942cde6a97572fcbb7dd2..ea4a588c1880d976848b3475d498d9870889cec7 100644 (file)
@@ -66,6 +66,7 @@ ns_server_create(isc_mem_t *mctx, ns_matchview_t matchingview,
        isc_quota_init(&sctx->tcpquota, 10);
        isc_quota_init(&sctx->recursionquota, 100);
        isc_quota_init(&sctx->updquota, 100);
+       isc_quota_init(&sctx->sig0checksquota, 1);
        ISC_LIST_INIT(sctx->http_quotas);
        isc_mutex_init(&sctx->http_quotas_lock);
 
@@ -134,6 +135,11 @@ ns_server_detach(ns_server_t **sctxp) {
                        isc_mem_put(sctx->mctx, altsecret, sizeof(*altsecret));
                }
 
+               if (sctx->sig0checksquota_exempt != NULL) {
+                       dns_acl_detach(&sctx->sig0checksquota_exempt);
+               }
+
+               isc_quota_destroy(&sctx->sig0checksquota);
                isc_quota_destroy(&sctx->updquota);
                isc_quota_destroy(&sctx->recursionquota);
                isc_quota_destroy(&sctx->tcpquota);
index 91799a774cd96cd3680b1f75fd1f801dc334f67b..f09b20045c83d9d836453c3d2bbbdb10b11aa8a8 100644 (file)
@@ -57,12 +57,13 @@ ns_server_t *sctx = NULL;
 
 static isc_result_t
 matchview(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
-         dns_message_t *message, dns_aclenv_t *env, isc_result_t *sigresultp,
-         dns_view_t **viewp) {
+         dns_message_t *message, dns_aclenv_t *env, ns_server_t *lsctx,
+         isc_result_t *sigresultp, dns_view_t **viewp) {
        UNUSED(srcaddr);
        UNUSED(destaddr);
        UNUSED(message);
        UNUSED(env);
+       UNUSED(lsctx);
        UNUSED(sigresultp);
        UNUSED(viewp);