mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-22 01:59:26 +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
|
||||
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;
|
||||
|
@ -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\
|
||||
|
@ -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,
|
||||
|
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-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>;
|
||||
|
@ -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) {
|
||||
|
@ -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 },
|
||||
|
266
lib/ns/client.c
266
lib/ns/client.c
@ -118,11 +118,27 @@
|
||||
|
||||
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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user