mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-29 05:28:00 +00:00
Add a quota for SIG(0) signature checks
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.
This commit is contained in:
parent
24e8cc7b38
commit
c7f79a0353
@ -2117,12 +2117,13 @@ cleanup:
|
|||||||
|
|
||||||
static isc_result_t
|
static isc_result_t
|
||||||
matchview(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
|
matchview(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
|
||||||
dns_message_t *message, dns_aclenv_t *env, isc_result_t *sigresultp,
|
dns_message_t *message, dns_aclenv_t *env, ns_server_t *lsctx,
|
||||||
dns_view_t **viewp) {
|
isc_result_t *sigresultp, dns_view_t **viewp) {
|
||||||
UNUSED(srcaddr);
|
UNUSED(srcaddr);
|
||||||
UNUSED(destaddr);
|
UNUSED(destaddr);
|
||||||
UNUSED(message);
|
UNUSED(message);
|
||||||
UNUSED(env);
|
UNUSED(env);
|
||||||
|
UNUSED(lsctx);
|
||||||
UNUSED(sigresultp);
|
UNUSED(sigresultp);
|
||||||
|
|
||||||
*viewp = view;
|
*viewp = view;
|
||||||
|
@ -109,6 +109,8 @@ options {\n\
|
|||||||
# session-keyfile \"" NAMED_LOCALSTATEDIR "/run/named/session.key\";\n\
|
# session-keyfile \"" NAMED_LOCALSTATEDIR "/run/named/session.key\";\n\
|
||||||
session-keyname local-ddns;\n\
|
session-keyname local-ddns;\n\
|
||||||
startup-notify-rate 20;\n\
|
startup-notify-rate 20;\n\
|
||||||
|
sig0checks-quota 1;\n\
|
||||||
|
sig0checks-quota-maxwait-ms 1500;\n\
|
||||||
statistics-file \"named.stats\";\n\
|
statistics-file \"named.stats\";\n\
|
||||||
tcp-advertised-timeout 300;\n\
|
tcp-advertised-timeout 300;\n\
|
||||||
tcp-clients 150;\n\
|
tcp-clients 150;\n\
|
||||||
|
@ -8412,6 +8412,8 @@ load_configuration(const char *filename, named_server_t *server,
|
|||||||
configure_server_quota(maps, "recursive-clients",
|
configure_server_quota(maps, "recursive-clients",
|
||||||
&server->sctx->recursionquota);
|
&server->sctx->recursionquota);
|
||||||
configure_server_quota(maps, "update-quota", &server->sctx->updquota);
|
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);
|
max = isc_quota_getmax(&server->sctx->recursionquota);
|
||||||
if (max > 1000) {
|
if (max > 1000) {
|
||||||
@ -8430,9 +8432,23 @@ load_configuration(const char *filename, named_server_t *server,
|
|||||||
} else {
|
} else {
|
||||||
softquota = (max * 90) / 100;
|
softquota = (max * 90) / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
isc_quota_soft(&server->sctx->recursionquota, softquota);
|
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
|
* Set "blackhole". Only legal at options level; there is
|
||||||
* no default.
|
* no default.
|
||||||
@ -10048,11 +10064,12 @@ shutdown_server(void *arg) {
|
|||||||
*/
|
*/
|
||||||
static isc_result_t
|
static isc_result_t
|
||||||
get_matching_view(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
|
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) {
|
isc_result_t *sigresult, dns_view_t **viewp) {
|
||||||
dns_view_t *view;
|
dns_view_t *view;
|
||||||
|
|
||||||
REQUIRE(message != NULL);
|
REQUIRE(message != NULL);
|
||||||
|
REQUIRE(sctx != NULL);
|
||||||
REQUIRE(sigresult != NULL);
|
REQUIRE(sigresult != NULL);
|
||||||
REQUIRE(viewp != NULL && *viewp == 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)
|
message->rdclass == dns_rdataclass_any)
|
||||||
{
|
{
|
||||||
const dns_name_t *tsig = NULL;
|
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);
|
*sigresult = dns_message_rechecksig(message, view);
|
||||||
|
if (sig0_qresult == ISC_R_SUCCESS) {
|
||||||
|
isc_quota_release(&sctx->sig0checksquota);
|
||||||
|
}
|
||||||
if (*sigresult == ISC_R_SUCCESS) {
|
if (*sigresult == ISC_R_SUCCESS) {
|
||||||
dns_tsigkey_t *tsigkey;
|
tsig = dns_tsigkey_identity(message->tsigkey);
|
||||||
|
|
||||||
tsigkey = message->tsigkey;
|
|
||||||
tsig = dns_tsigkey_identity(tsigkey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dns_acl_allowed(srcaddr, tsig, view->matchclients,
|
if (dns_acl_allowed(srcaddr, tsig, view->matchclients,
|
||||||
|
16
bin/tests/system/checkconf/bad-sig0checks-quota-exempt.conf
Normal file
16
bin/tests/system/checkconf/bad-sig0checks-quota-exempt.conf
Normal file
@ -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; };
|
||||||
|
};
|
20
bin/tests/system/checkconf/good-sig0checks-quota-exempt.conf
Normal file
20
bin/tests/system/checkconf/good-sig0checks-quota-exempt.conf
Normal file
@ -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; };
|
||||||
|
};
|
@ -277,6 +277,9 @@ options {
|
|||||||
sig-signing-signatures <integer>;
|
sig-signing-signatures <integer>;
|
||||||
sig-signing-type <integer>;
|
sig-signing-type <integer>;
|
||||||
sig-validity-interval <integer> [ <integer> ]; // obsolete
|
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
|
sortlist { <address_match_element>; ... }; // deprecated
|
||||||
stale-answer-client-timeout ( disabled | off | <integer> );
|
stale-answer-client-timeout ( disabled | off | <integer> );
|
||||||
stale-answer-enable <boolean>;
|
stale-answer-enable <boolean>;
|
||||||
|
@ -2073,6 +2073,21 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config,
|
|||||||
|
|
||||||
cfg_aclconfctx_create(mctx, &actx);
|
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;
|
obj = NULL;
|
||||||
(void)cfg_map_get(options, "listen-on", &obj);
|
(void)cfg_map_get(options, "listen-on", &obj);
|
||||||
if (obj != NULL) {
|
if (obj != NULL) {
|
||||||
|
@ -1361,6 +1361,9 @@ static cfg_clausedef_t options_clauses[] = {
|
|||||||
{ "session-keyalg", &cfg_type_astring, 0 },
|
{ "session-keyalg", &cfg_type_astring, 0 },
|
||||||
{ "session-keyfile", &cfg_type_qstringornone, 0 },
|
{ "session-keyfile", &cfg_type_qstringornone, 0 },
|
||||||
{ "session-keyname", &cfg_type_astring, 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 },
|
{ "sit-secret", NULL, CFG_CLAUSEFLAG_ANCIENT },
|
||||||
{ "stacksize", &cfg_type_size, CFG_CLAUSEFLAG_ANCIENT },
|
{ "stacksize", &cfg_type_size, CFG_CLAUSEFLAG_ANCIENT },
|
||||||
{ "startup-notify-rate", &cfg_type_uint32, 0 },
|
{ "startup-notify-rate", &cfg_type_uint32, 0 },
|
||||||
|
266
lib/ns/client.c
266
lib/ns/client.c
@ -118,11 +118,27 @@
|
|||||||
|
|
||||||
atomic_uint_fast64_t ns_client_requests = 0;
|
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
|
static void
|
||||||
clientmgr_destroy_cb(void *arg);
|
clientmgr_destroy_cb(void *arg);
|
||||||
static void
|
static void
|
||||||
ns_client_dumpmessage(ns_client_t *client, const char *reason);
|
ns_client_dumpmessage(ns_client_t *client, const char *reason);
|
||||||
static void
|
static void
|
||||||
|
ns_client_request_continue(void *arg);
|
||||||
|
static void
|
||||||
compute_cookie(ns_client_t *client, uint32_t when, const unsigned char *secret,
|
compute_cookie(ns_client_t *client, uint32_t when, const unsigned char *secret,
|
||||||
isc_buffer_t *buf);
|
isc_buffer_t *buf);
|
||||||
|
|
||||||
@ -1739,6 +1755,53 @@ ns__client_put_cb(void *client0) {
|
|||||||
ns_clientmgr_detach(&manager);
|
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)
|
* Handle an incoming request event from the socket (UDP case)
|
||||||
* or tcpmsg (TCP 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) {
|
isc_region_t *region, void *arg) {
|
||||||
ns_client_t *client = NULL;
|
ns_client_t *client = NULL;
|
||||||
isc_result_t result;
|
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;
|
dns_rdataset_t *opt = NULL;
|
||||||
const dns_name_t *signame = NULL;
|
|
||||||
bool ra; /* Recursion available. */
|
|
||||||
isc_netaddr_t netaddr;
|
isc_netaddr_t netaddr;
|
||||||
int match;
|
int match;
|
||||||
dns_messageid_t id;
|
dns_messageid_t id;
|
||||||
@ -1761,28 +1819,6 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
|
|||||||
bool notimp;
|
bool notimp;
|
||||||
size_t reqsize;
|
size_t reqsize;
|
||||||
dns_aclenv_t *env = NULL;
|
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) {
|
if (eresult != ISC_R_SUCCESS) {
|
||||||
return;
|
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);
|
(void)atomic_fetch_add_relaxed(&ns_client_requests, 1);
|
||||||
|
|
||||||
isc_buffer_init(&tbuffer, region->base, region->length);
|
isc_buffer_init(&client->tbuffer, region->base, region->length);
|
||||||
isc_buffer_add(&tbuffer, region->length);
|
isc_buffer_add(&client->tbuffer, region->length);
|
||||||
buffer = &tbuffer;
|
client->buffer = &client->tbuffer;
|
||||||
|
|
||||||
client->peeraddr = isc_nmhandle_peeraddr(handle);
|
client->peeraddr = isc_nmhandle_peeraddr(handle);
|
||||||
client->peeraddr_valid = true;
|
client->peeraddr_valid = true;
|
||||||
|
|
||||||
reqsize = isc_buffer_usedlength(buffer);
|
reqsize = isc_buffer_usedlength(client->buffer);
|
||||||
|
|
||||||
client->state = NS_CLIENTSTATE_WORKING;
|
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",
|
ISC_LOG_DEBUG(3), "%s request",
|
||||||
TCP_CLIENT(client) ? "TCP" : "UDP");
|
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) {
|
if (result != ISC_R_SUCCESS) {
|
||||||
/*
|
/*
|
||||||
* There isn't enough header to determine whether
|
* 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.
|
* 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) {
|
if (result != ISC_R_SUCCESS) {
|
||||||
/*
|
/*
|
||||||
* Parsing the request failed. Send a response
|
* 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);
|
client->destsockaddr = isc_nmhandle_localaddr(handle);
|
||||||
isc_netaddr_fromsockaddr(&client->destaddr, &client->destsockaddr);
|
isc_netaddr_fromsockaddr(&client->destaddr, &client->destsockaddr);
|
||||||
|
|
||||||
result = client->manager->sctx->matchingview(
|
result = ns_client_setup_view(client, &netaddr);
|
||||||
&netaddr, &client->destaddr, client->message, env, &sigresult,
|
if (result == ISC_R_QUOTA) {
|
||||||
&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));
|
|
||||||
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
|
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
|
||||||
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
|
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(5),
|
||||||
"no matching view in class '%s'", classname);
|
"client is starting to wait for quota");
|
||||||
ns_client_dumpmessage(client, "no matching view in class");
|
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_extendederror(client, DNS_EDE_PROHIBITED, NULL);
|
||||||
ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_REFUSED);
|
ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_REFUSED);
|
||||||
return;
|
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)) {
|
if (isc_nm_is_proxy_handle(client->handle)) {
|
||||||
char fmtbuf[ISC_SOCKADDR_FORMATSIZE] = { 0 };
|
char fmtbuf[ISC_SOCKADDR_FORMATSIZE] = { 0 };
|
||||||
isc_netaddr_t real_local_addr, real_peer_addr;
|
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",
|
"ACL",
|
||||||
fmtbuf);
|
fmtbuf);
|
||||||
}
|
}
|
||||||
isc_nm_bad_request(handle);
|
isc_nm_bad_request(client->handle);
|
||||||
return;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* allow by default */
|
/* allow by default */
|
||||||
@ -2161,8 +2278,8 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
|
|||||||
"'allow-proxy-on' ACL",
|
"'allow-proxy-on' ACL",
|
||||||
fmtbuf);
|
fmtbuf);
|
||||||
}
|
}
|
||||||
isc_nm_bad_request(handle);
|
isc_nm_bad_request(client->handle);
|
||||||
return;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2255,8 +2372,8 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
|
|||||||
if (!(client->message->tsigstatus == dns_tsigerror_badkey &&
|
if (!(client->message->tsigstatus == dns_tsigerror_badkey &&
|
||||||
client->message->opcode == dns_opcode_update))
|
client->message->opcode == dns_opcode_update))
|
||||||
{
|
{
|
||||||
ns_client_error(client, sigresult);
|
ns_client_error(client, client->sigresult);
|
||||||
return;
|
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,
|
dns_dt_send(client->view, dtmsgtype, &client->peeraddr,
|
||||||
&client->destsockaddr, transport_type, NULL,
|
&client->destsockaddr, transport_type, NULL,
|
||||||
&client->requesttime, NULL, buffer);
|
&client->requesttime, NULL, client->buffer);
|
||||||
#endif /* HAVE_DNSTAP */
|
#endif /* HAVE_DNSTAP */
|
||||||
|
|
||||||
ns_query_start(client, handle);
|
ns_query_start(client, client->handle);
|
||||||
break;
|
break;
|
||||||
case dns_opcode_update:
|
case dns_opcode_update:
|
||||||
CTRACE("update");
|
CTRACE("update");
|
||||||
#ifdef HAVE_DNSTAP
|
#ifdef HAVE_DNSTAP
|
||||||
dns_dt_send(client->view, DNS_DTTYPE_UQ, &client->peeraddr,
|
dns_dt_send(client->view, DNS_DTTYPE_UQ, &client->peeraddr,
|
||||||
&client->destsockaddr, transport_type, NULL,
|
&client->destsockaddr, transport_type, NULL,
|
||||||
&client->requesttime, NULL, buffer);
|
&client->requesttime, NULL, client->buffer);
|
||||||
#endif /* HAVE_DNSTAP */
|
#endif /* HAVE_DNSTAP */
|
||||||
ns_client_settimeout(client, 60);
|
ns_client_settimeout(client, 60);
|
||||||
ns_update_start(client, handle, sigresult);
|
ns_update_start(client, client->handle, client->sigresult);
|
||||||
break;
|
break;
|
||||||
case dns_opcode_notify:
|
case dns_opcode_notify:
|
||||||
CTRACE("notify");
|
CTRACE("notify");
|
||||||
ns_client_settimeout(client, 60);
|
ns_client_settimeout(client, 60);
|
||||||
ns_notify_start(client, handle);
|
ns_notify_start(client, client->handle);
|
||||||
break;
|
break;
|
||||||
case dns_opcode_iquery:
|
case dns_opcode_iquery:
|
||||||
CTRACE("iquery");
|
CTRACE("iquery");
|
||||||
@ -2368,6 +2485,15 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
|
|||||||
CTRACE("unknown opcode");
|
CTRACE("unknown opcode");
|
||||||
ns_client_error(client, DNS_R_NOTIMP);
|
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
|
isc_result_t
|
||||||
|
@ -167,6 +167,7 @@ struct ns_client {
|
|||||||
ns_clientmgr_t *manager;
|
ns_clientmgr_t *manager;
|
||||||
ns_clientstate_t state;
|
ns_clientstate_t state;
|
||||||
bool nodetach;
|
bool nodetach;
|
||||||
|
bool async;
|
||||||
unsigned int attributes;
|
unsigned int attributes;
|
||||||
dns_view_t *view;
|
dns_view_t *view;
|
||||||
dns_dispatch_t *dispatch;
|
dns_dispatch_t *dispatch;
|
||||||
@ -192,6 +193,9 @@ struct ns_client {
|
|||||||
isc_time_t tnow;
|
isc_time_t tnow;
|
||||||
dns_name_t signername; /*%< [T]SIG key name */
|
dns_name_t signername; /*%< [T]SIG key name */
|
||||||
dns_name_t *signer; /*%< NULL if not valid sig */
|
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;
|
isc_sockaddr_t peeraddr;
|
||||||
bool peeraddr_valid;
|
bool peeraddr_valid;
|
||||||
|
@ -64,9 +64,12 @@ typedef void (*ns_fuzzcb_t)(void);
|
|||||||
/*%
|
/*%
|
||||||
* Type for callback function to get the view that can answer a query.
|
* Type for callback function to get the view that can answer a query.
|
||||||
*/
|
*/
|
||||||
typedef isc_result_t (*ns_matchview_t)(
|
typedef isc_result_t (*ns_matchview_t)(isc_netaddr_t *srcaddr,
|
||||||
isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr, dns_message_t *message,
|
isc_netaddr_t *destaddr,
|
||||||
dns_aclenv_t *env, isc_result_t *sigresultp, dns_view_t **viewp);
|
dns_message_t *message,
|
||||||
|
dns_aclenv_t *env, ns_server_t *sctx,
|
||||||
|
isc_result_t *sigresultp,
|
||||||
|
dns_view_t **viewp);
|
||||||
|
|
||||||
/*%
|
/*%
|
||||||
* Server context.
|
* Server context.
|
||||||
@ -88,6 +91,9 @@ struct ns_server {
|
|||||||
isc_quota_t tcpquota;
|
isc_quota_t tcpquota;
|
||||||
isc_quota_t xfroutquota;
|
isc_quota_t xfroutquota;
|
||||||
isc_quota_t updquota;
|
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_LIST(isc_quota_t) http_quotas;
|
||||||
isc_mutex_t http_quotas_lock;
|
isc_mutex_t http_quotas_lock;
|
||||||
|
|
||||||
|
@ -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->tcpquota, 10);
|
||||||
isc_quota_init(&sctx->recursionquota, 100);
|
isc_quota_init(&sctx->recursionquota, 100);
|
||||||
isc_quota_init(&sctx->updquota, 100);
|
isc_quota_init(&sctx->updquota, 100);
|
||||||
|
isc_quota_init(&sctx->sig0checksquota, 1);
|
||||||
ISC_LIST_INIT(sctx->http_quotas);
|
ISC_LIST_INIT(sctx->http_quotas);
|
||||||
isc_mutex_init(&sctx->http_quotas_lock);
|
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));
|
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->updquota);
|
||||||
isc_quota_destroy(&sctx->recursionquota);
|
isc_quota_destroy(&sctx->recursionquota);
|
||||||
isc_quota_destroy(&sctx->tcpquota);
|
isc_quota_destroy(&sctx->tcpquota);
|
||||||
|
@ -57,12 +57,13 @@ ns_server_t *sctx = NULL;
|
|||||||
|
|
||||||
static isc_result_t
|
static isc_result_t
|
||||||
matchview(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
|
matchview(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
|
||||||
dns_message_t *message, dns_aclenv_t *env, isc_result_t *sigresultp,
|
dns_message_t *message, dns_aclenv_t *env, ns_server_t *lsctx,
|
||||||
dns_view_t **viewp) {
|
isc_result_t *sigresultp, dns_view_t **viewp) {
|
||||||
UNUSED(srcaddr);
|
UNUSED(srcaddr);
|
||||||
UNUSED(destaddr);
|
UNUSED(destaddr);
|
||||||
UNUSED(message);
|
UNUSED(message);
|
||||||
UNUSED(env);
|
UNUSED(env);
|
||||||
|
UNUSED(lsctx);
|
||||||
UNUSED(sigresultp);
|
UNUSED(sigresultp);
|
||||||
UNUSED(viewp);
|
UNUSED(viewp);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user