From 9d308c623699ec4f2eb38515e9350698ae0151fc Mon Sep 17 00:00:00 2001 From: Bob Halley Date: Wed, 18 Aug 1999 04:23:39 +0000 Subject: [PATCH] use only one database version per query --- bin/named/client.c | 2 + bin/named/include/named/client.h | 2 + bin/named/include/named/query.h | 8 ++ bin/named/query.c | 154 +++++++++++++++++++++++++++++-- 4 files changed, 159 insertions(+), 7 deletions(-) diff --git a/bin/named/client.c b/bin/named/client.c index 951bbf5e86..c44947826a 100644 --- a/bin/named/client.c +++ b/bin/named/client.c @@ -404,6 +404,8 @@ client_request(isc_task_t *task, isc_event_t *event) { CTRACE("request"); client->state = ns_clientstate_working; + if (isc_stdtime_get(&client->requesttime) != ISC_R_SUCCESS) + client->requesttime = 0; if (result != ISC_R_SUCCESS) { if (TCP_CLIENT(client)) diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h index 625cd9b71e..1d62d339cb 100644 --- a/bin/named/include/named/client.h +++ b/bin/named/include/named/client.h @@ -19,6 +19,7 @@ #define NS_CLIENT_H 1 #include +#include #include #include @@ -64,6 +65,7 @@ struct ns_client { isc_mempool_t * sendbufs; void (*next)(ns_client_t *, isc_result_t); ns_query_t query; + isc_stdtime_t requesttime; ISC_LINK(struct ns_client) link; }; diff --git a/bin/named/include/named/query.h b/bin/named/include/named/query.h index 29b4c74301..3da0fea86d 100644 --- a/bin/named/include/named/query.h +++ b/bin/named/include/named/query.h @@ -25,6 +25,12 @@ #include +typedef struct ns_dbversion { + dns_db_t *db; + dns_dbversion_t *version; + ISC_LINK(struct ns_dbversion) link; +} ns_dbversion_t; + struct ns_query { unsigned int attributes; dns_name_t * qname; @@ -33,6 +39,8 @@ struct ns_query { ISC_LIST(isc_dynbuffer_t) namebufs; ISC_LIST(dns_name_t) tmpnames; ISC_LIST(dns_rdataset_t) tmprdatasets; + ISC_LIST(ns_dbversion_t) activeversions; + ISC_LIST(ns_dbversion_t) freeversions; }; #define NS_QUERYATTR_RECURSIONOK 0x01 diff --git a/bin/named/query.c b/bin/named/query.c index 65c8e30d59..8a4aa17897 100644 --- a/bin/named/query.c +++ b/bin/named/query.c @@ -50,6 +50,42 @@ static inline void query_reset(ns_client_t *client, isc_boolean_t everything) { isc_dynbuffer_t *dbuf, *dbuf_next; + ns_dbversion_t *dbversion, *dbversion_next; + unsigned int i; + + + /* + * Cleanup any active versions. + */ + for (dbversion = ISC_LIST_HEAD(client->query.activeversions); + dbversion != NULL; + dbversion = dbversion_next) { + dbversion_next = ISC_LIST_NEXT(dbversion, link); + dns_db_closeversion(dbversion->db, &dbversion->version, + ISC_FALSE); + dns_db_detach(&dbversion->db); + ISC_LIST_APPEND(client->query.freeversions, dbversion, link); + } + ISC_LIST_INIT(client->query.activeversions); + + /* + * Clean up free versions. + */ + for (dbversion = ISC_LIST_HEAD(client->query.freeversions), i = 0; + dbversion != NULL; + dbversion = dbversion_next, i++) { + dbversion_next = ISC_LIST_NEXT(dbversion, link); + /* + * If we're not freeing everything, we keep the first three + * dbversions structures around. + */ + if (i > 3 || everything) { + ISC_LIST_UNLINK(client->query.freeversions, dbversion, + link); + isc_mem_put(client->mctx, dbversion, + sizeof *dbversion); + } + } for (dbuf = ISC_LIST_HEAD(client->query.namebufs); dbuf != NULL; @@ -89,8 +125,6 @@ query_newnamebuf(ns_client_t *client) { isc_dynbuffer_t *dbuf; isc_result_t result; - REQUIRE(NS_CLIENT_VALID(client)); - dbuf = NULL; result = isc_dynbuffer_allocate(client->mctx, &dbuf, 1024, ISC_BUFFERTYPE_BINARY); @@ -201,13 +235,96 @@ query_newrdataset(ns_client_t *client) { return (rdataset); } +static inline isc_result_t +query_newdbversion(ns_client_t *client, unsigned int n) { + unsigned int i; + ns_dbversion_t *dbversion; + + for (i = 0; i < n; i++) { + dbversion = isc_mem_get(client->mctx, sizeof *dbversion); + if (dbversion != NULL) { + dbversion->db = NULL; + dbversion->version = NULL; + ISC_LIST_APPEND(client->query.freeversions, dbversion, + link); + } else { + /* + * We only return ISC_R_NOMEMORY if we couldn't + * allocate anything. + */ + if (i == 0) + return (ISC_R_NOMEMORY); + else + return (ISC_R_SUCCESS); + } + } + + return (ISC_R_SUCCESS); +} + +static inline ns_dbversion_t * +query_getdbversion(ns_client_t *client) { + isc_result_t result; + ns_dbversion_t *dbversion; + + if (ISC_LIST_EMPTY(client->query.freeversions)) { + result = query_newdbversion(client, 1); + if (result != ISC_R_SUCCESS) + return (NULL); + } + dbversion = ISC_LIST_HEAD(client->query.freeversions); + INSIST(dbversion != NULL); + ISC_LIST_UNLINK(client->query.freeversions, dbversion, link); + + return (dbversion); +} + isc_result_t ns_query_init(ns_client_t *client) { + isc_result_t result; + ISC_LIST_INIT(client->query.namebufs); + ISC_LIST_INIT(client->query.activeversions); + ISC_LIST_INIT(client->query.freeversions); query_reset(client, ISC_FALSE); + result = query_newdbversion(client, 3); + if (result != ISC_R_SUCCESS) + return (result); return (query_newnamebuf(client)); } +static inline dns_dbversion_t * +query_findversion(ns_client_t *client, dns_db_t *db) { + ns_dbversion_t *dbversion; + + /* + * We may already have done a query related to this + * database. If so, we must be sure to make subsequent + * queries from the same version. + */ + for (dbversion = ISC_LIST_HEAD(client->query.activeversions); + dbversion != NULL; + dbversion = ISC_LIST_NEXT(dbversion, link)) { + if (dbversion->db == db) + break; + } + if (dbversion == NULL) { + /* + * This is a new zone for this query. Add it to + * the active list. + */ + dbversion = query_getdbversion(client); + if (dbversion == NULL) + return (NULL); + dns_db_attach(db, &dbversion->db); + dns_db_currentversion(db, &dbversion->version); + ISC_LIST_APPEND(client->query.activeversions, + dbversion, link); + } + + return (dbversion->version); +} + static isc_result_t query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t type) { ns_client_t *client = arg; @@ -219,6 +336,7 @@ query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t type) { dns_section_t section; isc_dynbuffer_t *dbuf; isc_buffer_t b; + dns_dbversion_t *version; REQUIRE(NS_CLIENT_VALID(client)); REQUIRE(type != dns_rdatatype_any); @@ -236,6 +354,7 @@ query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t type) { fname = NULL; rdataset = NULL; db = NULL; + version = NULL; node = NULL; /* @@ -256,12 +375,21 @@ query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t type) { if (result != ISC_R_SUCCESS && result != DNS_R_PARTIALMATCH) goto cleanup; + /* + * Get the current version of this database. + */ + if (dns_db_iszone(db)) { + version = query_findversion(client, db); + if (version == NULL) + goto cleanup; + } + /* * Now look for an answer in the database. */ node = NULL; - result = dns_db_find(db, name, NULL, type, client->query.dboptions, - 0, &node, fname, rdataset); + result = dns_db_find(db, name, version, type, client->query.dboptions, + client->requesttime, &node, fname, rdataset); switch (result) { case DNS_R_SUCCESS: case DNS_R_GLUE: @@ -534,6 +662,7 @@ query_find(ns_client_t *client) { isc_buffer_t b; isc_result_t result, eresult; dns_fixedname_t fixed; + dns_dbversion_t *version; /* * One-time initialization. @@ -550,6 +679,7 @@ query_find(ns_client_t *client) { rdataset = NULL; node = NULL; db = NULL; + version = NULL; if (client->view->cachedb == NULL || client->view->resolver == NULL) { @@ -593,9 +723,19 @@ query_find(ns_client_t *client) { } is_zone = dns_db_iszone(db); - if (is_zone) + if (is_zone) { auth = ISC_TRUE; + /* + * Get the current version of this database. + */ + version = query_findversion(client, db); + if (version == NULL) { + QUERY_ERROR(DNS_R_SERVFAIL); + goto cleanup; + } + } + /* * Find the first unanswered type in the question section. */ @@ -658,8 +798,8 @@ query_find(ns_client_t *client) { /* * Now look for an answer in the database. */ - result = dns_db_find(db, client->query.qname, NULL, type, 0, 0, &node, - fname, rdataset); + result = dns_db_find(db, client->query.qname, version, type, 0, + client->requesttime, &node, fname, rdataset); switch (result) { case DNS_R_SUCCESS: case DNS_R_ZONECUT: