From b19619260fbd447b0fe3e709b2cc8ae38f27ec3f Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 24 Oct 2001 03:10:18 +0000 Subject: [PATCH] 1069. [func] Kill oldest recursive query when recursive query quota is exhausted. --- CHANGES | 2 ++ bin/named/client.c | 40 ++++++++++++++++++++++++---- bin/named/include/named/client.h | 9 ++++++- bin/named/include/named/query.h | 6 ++++- bin/named/query.c | 45 +++++++++++++++++++++++++------- bin/named/server.c | 3 ++- 6 files changed, 88 insertions(+), 17 deletions(-) diff --git a/CHANGES b/CHANGES index 993fe4cc00..2416ffcd4d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ +1069. [func] Kill oldest recursive query when recursive query quota is exhausted. + 1068. [bug] errno could be overwritten by catgets(). [RT #1921] 1067. [func] Allow quotas to be soft, isc_quota_soft(). diff --git a/bin/named/client.c b/bin/named/client.c index 63e58460ab..78d3a98ea2 100644 --- a/bin/named/client.c +++ b/bin/named/client.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: client.c,v 1.189 2001/10/13 01:10:26 gson Exp $ */ +/* $Id: client.c,v 1.190 2001/10/24 03:10:13 marka Exp $ */ #include @@ -95,6 +95,7 @@ struct ns_clientmgr { /* Locked by lock. */ isc_boolean_t exiting; client_list_t active; /* Active clients */ + client_list_t recursing; /* Recursing clients */ client_list_t inactive; /* To be recycled */ }; @@ -173,6 +174,27 @@ static void client_start(isc_task_t *task, isc_event_t *event); static void client_request(isc_task_t *task, isc_event_t *event); static void ns_client_dumpmessage(ns_client_t *client, const char *reason); +void +ns_client_recursing(ns_client_t *client, isc_boolean_t killoldest) { + ns_client_t *oldest; + REQUIRE(NS_CLIENT_VALID(client)); + + LOCK(&client->manager->lock); + if (killoldest) { + oldest = ISC_LIST_HEAD(client->manager->recursing); + if (oldest != NULL) { + ns_query_cancel(oldest); + ISC_LIST_UNLINK(*oldest->list, oldest, link); + ISC_LIST_APPEND(client->manager->active, oldest, link); + oldest->list = &client->manager->active; + } + } + ISC_LIST_UNLINK(*client->list, client, link); + ISC_LIST_APPEND(client->manager->recursing, client, link); + client->list = &client->manager->recursing; + UNLOCK(&client->manager->lock); +} + /* * Enter the inactive state. * @@ -201,7 +223,7 @@ client_deactivate(ns_client_t *client) { client->mortal = ISC_FALSE; LOCK(&client->manager->lock); - ISC_LIST_UNLINK(client->manager->active, client, link); + ISC_LIST_UNLINK(*client->list, client, link); ISC_LIST_APPEND(client->manager->inactive, client, link); client->list = &client->manager->inactive; UNLOCK(&client->manager->lock); @@ -251,7 +273,8 @@ client_free(ns_client_t *client) { client->list = NULL; if (manager->exiting && ISC_LIST_EMPTY(manager->active) && - ISC_LIST_EMPTY(manager->inactive)) + ISC_LIST_EMPTY(manager->inactive) && + ISC_LIST_EMPTY(manager->recursing)) need_clientmgr_destroy = ISC_TRUE; UNLOCK(&manager->lock); } @@ -506,7 +529,6 @@ client_shutdown(isc_task_t *task, isc_event_t *event) { (void)exit_check(client); } - static void ns_client_endrequest(ns_client_t *client) { INSIST(client->naccepts == 0); @@ -1860,6 +1882,7 @@ static void clientmgr_destroy(ns_clientmgr_t *manager) { REQUIRE(ISC_LIST_EMPTY(manager->active)); REQUIRE(ISC_LIST_EMPTY(manager->inactive)); + REQUIRE(ISC_LIST_EMPTY(manager->recursing)); MTRACE("clientmgr_destroy"); @@ -1889,6 +1912,7 @@ ns_clientmgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, manager->exiting = ISC_FALSE; ISC_LIST_INIT(manager->active); ISC_LIST_INIT(manager->inactive); + ISC_LIST_INIT(manager->recursing); manager->magic = MANAGER_MAGIC; MTRACE("create"); @@ -1919,6 +1943,11 @@ ns_clientmgr_destroy(ns_clientmgr_t **managerp) { manager->exiting = ISC_TRUE; + for (client = ISC_LIST_HEAD(manager->recursing); + client != NULL; + client = ISC_LIST_NEXT(client, link)) + isc_task_shutdown(client->task); + for (client = ISC_LIST_HEAD(manager->active); client != NULL; client = ISC_LIST_NEXT(client, link)) @@ -1930,7 +1959,8 @@ ns_clientmgr_destroy(ns_clientmgr_t **managerp) { isc_task_shutdown(client->task); if (ISC_LIST_EMPTY(manager->active) && - ISC_LIST_EMPTY(manager->inactive)) + ISC_LIST_EMPTY(manager->inactive) && + ISC_LIST_EMPTY(manager->recursing)) need_destroy = ISC_TRUE; UNLOCK(&manager->lock); diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h index a7e88a6b9a..a9178db334 100644 --- a/bin/named/include/named/client.h +++ b/bin/named/include/named/client.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: client.h,v 1.61 2001/10/01 18:53:58 gson Exp $ */ +/* $Id: client.h,v 1.62 2001/10/24 03:10:17 marka Exp $ */ #ifndef NAMED_CLIENT_H #define NAMED_CLIENT_H 1 @@ -306,4 +306,11 @@ void ns_client_aclmsg(const char *msg, dns_name_t *name, dns_rdataclass_t rdclass, char *buf, size_t len); +void +ns_client_recursing(ns_client_t *client, isc_boolean_t killoldest); +/* + * Add client to end of recursing list. If 'killoldest' is true + * kill the oldest recursive client (list head). + */ + #endif /* NAMED_CLIENT_H */ diff --git a/bin/named/include/named/query.h b/bin/named/include/named/query.h index c3dcab9055..6ff646bc41 100644 --- a/bin/named/include/named/query.h +++ b/bin/named/include/named/query.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: query.h,v 1.30 2001/09/21 19:08:06 gson Exp $ */ +/* $Id: query.h,v 1.31 2001/10/24 03:10:18 marka Exp $ */ #ifndef NAMED_QUERY_H #define NAMED_QUERY_H 1 @@ -49,6 +49,7 @@ struct ns_query { dns_zone_t * authzone; isc_boolean_t authdbset; isc_boolean_t isreferral; + isc_mutex_t fetchlock; dns_fetch_t * fetch; dns_a6context_t a6ctx; isc_bufferlist_t namebufs; @@ -84,4 +85,7 @@ ns_query_free(ns_client_t *client); void ns_query_start(ns_client_t *client); +void +ns_query_cancel(ns_client_t *client); + #endif /* NAMED_QUERY_H */ diff --git a/bin/named/query.c b/bin/named/query.c index 755af7b1e8..f39d2400f2 100644 --- a/bin/named/query.c +++ b/bin/named/query.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: query.c,v 1.205 2001/10/23 06:04:37 marka Exp $ */ +/* $Id: query.c,v 1.206 2001/10/24 03:10:14 marka Exp $ */ #include @@ -204,6 +204,17 @@ query_maybeputqname(ns_client_t *client) { } } +void +ns_query_cancel(ns_client_t *client) { + LOCK(&client->query.fetchlock); + if (client->query.fetch != NULL) { + dns_resolver_cancelfetch(client->query.fetch); + + client->query.fetch = NULL; + } + UNLOCK(&client->query.fetchlock); +} + static inline void query_reset(ns_client_t *client, isc_boolean_t everything) { isc_buffer_t *dbuf, *dbuf_next; @@ -217,11 +228,7 @@ query_reset(ns_client_t *client, isc_boolean_t everything) { /* * Cancel the fetch if it's running. */ - if (client->query.fetch != NULL) { - dns_resolver_cancelfetch(client->query.fetch); - - client->query.fetch = NULL; - } + ns_query_cancel(client); /* * Cleanup any active versions. @@ -509,6 +516,9 @@ ns_query_init(ns_client_t *client) { client->query.restarts = 0; client->query.timerset = ISC_FALSE; client->query.qname = NULL; + result = isc_mutex_init(&client->query.fetchlock); + if (result != ISC_R_SUCCESS) + return (result); client->query.fetch = NULL; client->query.authdb = NULL; client->query.authzone = NULL; @@ -516,13 +526,16 @@ ns_query_init(ns_client_t *client) { client->query.isreferral = ISC_FALSE; query_reset(client, ISC_FALSE); result = query_newdbversion(client, 3); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { + DESTROYLOCK(&client->query.fetchlock); return (result); + } dns_a6_init(&client->query.a6ctx, query_simplefind, query_adda6rrset, NULL, NULL, client); result = query_newnamebuf(client); if (result != ISC_R_SUCCESS) { ns_dbversion_t *dbversion; + DESTROYLOCK(&client->query.fetchlock); for (dbversion = ISC_LIST_HEAD(client->query.freeversions); dbversion != NULL; dbversion = ISC_LIST_HEAD(client->query.freeversions)) { @@ -2043,6 +2056,7 @@ query_resume(isc_task_t *task, isc_event_t *event) { REQUIRE(task == client->task); REQUIRE(RECURSING(client)); + LOCK(&client->query.fetchlock); if (client->query.fetch != NULL) { /* * This is the fetch we've been waiting for. @@ -2061,6 +2075,7 @@ query_resume(isc_task_t *task, isc_event_t *event) { */ fetch_cancelled = ISC_TRUE; } + UNLOCK(&client->query.fetchlock); INSIST(client->query.fetch == NULL); client->query.attributes &= ~NS_QUERYATTR_RECURSING; @@ -2080,7 +2095,10 @@ query_resume(isc_task_t *task, isc_event_t *event) { if (devent->sigrdataset != NULL) query_putrdataset(client, &devent->sigrdataset); isc_event_free(&event); - query_next(client, ISC_R_CANCELED); + if (fetch_cancelled) + query_error(client, DNS_R_SERVFAIL); + else + query_next(client, ISC_R_CANCELED); /* * This may destroy the client. */ @@ -2109,9 +2127,17 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain, * response. */ if (! client->mortal) { + isc_boolean_t killoldest = ISC_FALSE; result = isc_quota_attach(&ns_g_server->recursionquota, &client->recursionquota); - if (result == ISC_R_SUCCESS) + if (result == ISC_R_SOFTQUOTA) { + ns_client_log(client, NS_LOGCATEGORY_CLIENT, + NS_LOGMODULE_QUERY, ISC_LOG_WARNING, + "killing oldest recursive client: %s", + isc_result_totext(result)); + killoldest = ISC_TRUE; + } + if (result == ISC_R_SUCCESS || result == ISC_R_SOFTQUOTA) result = ns_client_replace(client); if (result != ISC_R_SUCCESS) { ns_client_log(client, NS_LOGCATEGORY_CLIENT, @@ -2120,6 +2146,7 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain, isc_result_totext(result)); return (result); } + ns_client_recursing(client, killoldest); } /* diff --git a/bin/named/server.c b/bin/named/server.c index a33360eeef..8aaaeb36a1 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: server.c,v 1.352 2001/10/17 19:34:07 gson Exp $ */ +/* $Id: server.c,v 1.353 2001/10/24 03:10:15 marka Exp $ */ #include @@ -2387,6 +2387,7 @@ ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) { RUNTIME_CHECK(result == ISC_R_SUCCESS); result = isc_quota_init(&server->recursionquota, 100); RUNTIME_CHECK(result == ISC_R_SUCCESS); + isc_quota_soft(&server->recursionquota, ISC_TRUE); result = dns_aclenv_init(mctx, &server->aclenv); RUNTIME_CHECK(result == ISC_R_SUCCESS);