1999-11-29 17:58:39 +00:00
|
|
|
/*
|
2011-03-03 23:47:32 +00:00
|
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
1999-11-29 17:58:39 +00:00
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: MPL-2.0
|
2021-06-03 08:37:05 +02:00
|
|
|
*
|
1999-11-29 17:58:39 +00:00
|
|
|
* 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/.
|
2018-02-23 09:53:12 +01:00
|
|
|
*
|
1999-11-29 17:58:39 +00:00
|
|
|
* See the COPYRIGHT file distributed with this work for additional
|
2012-05-14 10:06:05 -07:00
|
|
|
* information regarding copyright ownership.
|
1999-11-29 17:58:39 +00:00
|
|
|
*/
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*! \file */
|
1999-11-29 17:58:39 +00:00
|
|
|
|
2018-03-28 14:56:40 +02:00
|
|
|
#include <inttypes.h>
|
2018-03-28 14:19:37 +02:00
|
|
|
#include <stdbool.h>
|
2018-03-28 14:56:40 +02:00
|
|
|
|
2022-10-26 22:36:04 -07:00
|
|
|
#include <isc/loop.h>
|
2000-05-08 14:38:29 +00:00
|
|
|
#include <isc/mem.h>
|
2019-05-17 13:47:00 +02:00
|
|
|
#include <isc/refcount.h>
|
2021-10-04 17:14:53 +02:00
|
|
|
#include <isc/result.h>
|
2012-05-14 10:06:05 -07:00
|
|
|
#include <isc/stats.h>
|
2008-05-01 18:23:07 +00:00
|
|
|
#include <isc/string.h>
|
2000-04-25 19:35:39 +00:00
|
|
|
#include <isc/time.h>
|
2000-05-08 14:38:29 +00:00
|
|
|
#include <isc/timer.h>
|
2000-01-04 23:24:13 +00:00
|
|
|
#include <isc/util.h>
|
1999-11-29 17:58:39 +00:00
|
|
|
|
|
|
|
#include <dns/cache.h>
|
|
|
|
#include <dns/db.h>
|
|
|
|
#include <dns/dbiterator.h>
|
|
|
|
#include <dns/log.h>
|
2001-01-12 22:22:17 +00:00
|
|
|
#include <dns/masterdump.h>
|
2001-11-27 03:10:32 +00:00
|
|
|
#include <dns/rdata.h>
|
|
|
|
#include <dns/rdataset.h>
|
|
|
|
#include <dns/rdatasetiter.h>
|
2012-05-14 10:06:05 -07:00
|
|
|
#include <dns/stats.h>
|
1999-11-29 17:58:39 +00:00
|
|
|
|
2019-06-24 12:21:47 +02:00
|
|
|
#ifdef HAVE_JSON_C
|
|
|
|
#include <json_object.h>
|
|
|
|
#endif /* HAVE_JSON_C */
|
|
|
|
|
2019-06-24 14:25:55 +02:00
|
|
|
#ifdef HAVE_LIBXML2
|
|
|
|
#include <libxml/xmlwriter.h>
|
|
|
|
#define ISC_XMLCHAR (const xmlChar *)
|
|
|
|
#endif /* HAVE_LIBXML2 */
|
|
|
|
|
2008-05-01 18:23:07 +00:00
|
|
|
#define CACHE_MAGIC ISC_MAGIC('$', '$', '$', '$')
|
|
|
|
#define VALID_CACHE(cache) ISC_MAGIC_VALID(cache, CACHE_MAGIC)
|
1999-11-29 17:58:39 +00:00
|
|
|
|
2022-11-02 11:40:19 +01:00
|
|
|
/*
|
2005-04-27 04:57:32 +00:00
|
|
|
* DNS_CACHE_MINSIZE is how many bytes is the floor for
|
2022-11-02 11:40:19 +01:00
|
|
|
* dns_cache_setcachesize().
|
2005-04-27 04:57:32 +00:00
|
|
|
*/
|
2013-03-05 23:41:22 +11:00
|
|
|
#define DNS_CACHE_MINSIZE 2097152U /*%< Bytes. 2097152 = 2 MB */
|
2001-06-05 22:27:51 +00:00
|
|
|
|
1999-11-29 17:58:39 +00:00
|
|
|
/***
|
2008-05-01 18:23:07 +00:00
|
|
|
*** Types
|
1999-11-29 17:58:39 +00:00
|
|
|
***/
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-11-29 17:58:39 +00:00
|
|
|
* The actual cache object.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct dns_cache {
|
2008-02-07 23:46:54 +00:00
|
|
|
/* Unlocked. */
|
2008-05-01 18:23:07 +00:00
|
|
|
unsigned int magic;
|
|
|
|
isc_mutex_t lock;
|
2024-03-27 11:32:25 +11:00
|
|
|
isc_mem_t *mctx; /* Memory context for the dns_cache object */
|
2011-03-03 04:42:25 +00:00
|
|
|
isc_mem_t *hmctx; /* Heap memory */
|
2024-02-16 11:40:26 +11:00
|
|
|
isc_mem_t *tmctx; /* Tree memory */
|
2024-03-06 18:14:32 +01:00
|
|
|
isc_loop_t *loop;
|
2009-01-09 22:24:37 +00:00
|
|
|
char *name;
|
2019-05-17 13:47:00 +02:00
|
|
|
isc_refcount_t references;
|
2008-02-07 23:46:54 +00:00
|
|
|
|
|
|
|
/* Locked by 'lock'. */
|
2008-05-01 18:23:07 +00:00
|
|
|
dns_rdataclass_t rdclass;
|
|
|
|
dns_db_t *db;
|
2013-02-28 09:29:12 -08:00
|
|
|
size_t size;
|
2017-09-06 09:58:29 +10:00
|
|
|
dns_ttl_t serve_stale_ttl;
|
2020-11-10 13:50:54 -03:00
|
|
|
dns_ttl_t serve_stale_refresh;
|
2012-05-14 10:06:05 -07:00
|
|
|
isc_stats_t *stats;
|
1999-11-29 17:58:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/***
|
2008-05-01 18:23:07 +00:00
|
|
|
*** Functions
|
1999-11-29 17:58:39 +00:00
|
|
|
***/
|
|
|
|
|
2021-10-11 13:43:12 +02:00
|
|
|
static isc_result_t
|
2024-02-16 11:40:26 +11:00
|
|
|
cache_create_db(dns_cache_t *cache, dns_db_t **dbp, isc_mem_t **tmctxp,
|
|
|
|
isc_mem_t **hmctxp) {
|
2017-09-06 09:58:29 +10:00
|
|
|
isc_result_t result;
|
2022-11-02 11:40:19 +01:00
|
|
|
char *argv[1] = { 0 };
|
2024-02-16 11:40:26 +11:00
|
|
|
dns_db_t *db = NULL;
|
|
|
|
isc_mem_t *tmctx = NULL, *hmctx = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This will be the cache memory context, which is subject
|
|
|
|
* to cleaning when the configured memory limits are exceeded.
|
|
|
|
*/
|
|
|
|
isc_mem_create(&tmctx);
|
|
|
|
isc_mem_setname(tmctx, "cache");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This will be passed to RBTDB to use for heaps. This is separate
|
|
|
|
* from the main cache memory because it can grow quite large under
|
|
|
|
* heavy load and could otherwise cause the cache to be cleaned too
|
|
|
|
* aggressively.
|
|
|
|
*/
|
|
|
|
isc_mem_create(&hmctx);
|
|
|
|
isc_mem_setname(hmctx, "cache_heap");
|
2022-11-02 11:40:19 +01:00
|
|
|
|
|
|
|
/*
|
2024-03-06 17:54:37 -08:00
|
|
|
* For databases of type "qpcache" or "rbt" (which are the
|
|
|
|
* only cache implementations currently in existence) we pass
|
|
|
|
* hmctx to dns_db_create() via argv[0].
|
2022-11-02 11:40:19 +01:00
|
|
|
*/
|
2024-02-16 11:40:26 +11:00
|
|
|
argv[0] = (char *)hmctx;
|
|
|
|
result = dns_db_create(tmctx, CACHEDB_DEFAULT, dns_rootname,
|
|
|
|
dns_dbtype_cache, cache->rdclass, 1, argv, &db);
|
2024-05-24 11:35:40 +02:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
2024-02-16 11:40:26 +11:00
|
|
|
goto cleanup_mctx;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2024-02-16 11:40:26 +11:00
|
|
|
result = dns_db_setcachestats(db, cache->stats);
|
2024-05-24 11:35:40 +02:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
2024-02-16 11:40:26 +11:00
|
|
|
goto cleanup_db;
|
2024-05-24 11:35:40 +02:00
|
|
|
}
|
2024-02-16 11:40:26 +11:00
|
|
|
dns_db_setservestalettl(db, cache->serve_stale_ttl);
|
|
|
|
dns_db_setservestalerefresh(db, cache->serve_stale_refresh);
|
|
|
|
dns_db_setloop(db, cache->loop);
|
|
|
|
*dbp = db;
|
|
|
|
*hmctxp = hmctx;
|
|
|
|
*tmctxp = tmctx;
|
2024-05-24 11:35:40 +02:00
|
|
|
|
|
|
|
return (ISC_R_SUCCESS);
|
2024-02-16 11:40:26 +11:00
|
|
|
|
|
|
|
cleanup_db:
|
|
|
|
dns_db_detach(&db);
|
|
|
|
cleanup_mctx:
|
|
|
|
isc_mem_detach(&hmctx);
|
|
|
|
isc_mem_detach(&tmctx);
|
|
|
|
|
|
|
|
return (result);
|
2024-05-24 11:35:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cache_destroy(dns_cache_t *cache) {
|
|
|
|
isc_stats_detach(&cache->stats);
|
|
|
|
isc_mutex_destroy(&cache->lock);
|
|
|
|
isc_mem_free(cache->mctx, cache->name);
|
|
|
|
isc_loop_detach(&cache->loop);
|
2024-02-16 11:40:26 +11:00
|
|
|
if (cache->hmctx != NULL) {
|
|
|
|
isc_mem_detach(&cache->hmctx);
|
|
|
|
}
|
|
|
|
if (cache->tmctx != NULL) {
|
|
|
|
isc_mem_detach(&cache->tmctx);
|
|
|
|
}
|
2024-05-24 11:35:40 +02:00
|
|
|
isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
|
2001-04-11 20:37:50 +00:00
|
|
|
}
|
|
|
|
|
1999-12-22 17:37:31 +00:00
|
|
|
isc_result_t
|
2022-10-26 22:36:04 -07:00
|
|
|
dns_cache_create(isc_loopmgr_t *loopmgr, dns_rdataclass_t rdclass,
|
2024-03-27 11:32:25 +11:00
|
|
|
const char *cachename, isc_mem_t *mctx, dns_cache_t **cachep) {
|
2008-02-07 23:46:54 +00:00
|
|
|
isc_result_t result;
|
2022-11-02 11:40:19 +01:00
|
|
|
dns_cache_t *cache = NULL;
|
2008-02-07 23:46:54 +00:00
|
|
|
|
2022-10-26 22:36:04 -07:00
|
|
|
REQUIRE(loopmgr != NULL);
|
2009-01-09 22:24:37 +00:00
|
|
|
REQUIRE(cachename != NULL);
|
2022-10-26 22:36:04 -07:00
|
|
|
REQUIRE(cachep != NULL && *cachep == NULL);
|
2008-02-07 23:46:54 +00:00
|
|
|
|
2022-11-02 11:40:19 +01:00
|
|
|
cache = isc_mem_get(mctx, sizeof(*cache));
|
|
|
|
*cache = (dns_cache_t){
|
|
|
|
.rdclass = rdclass,
|
|
|
|
.name = isc_mem_strdup(mctx, cachename),
|
2024-03-06 18:14:32 +01:00
|
|
|
.loop = isc_loop_ref(isc_loop_main(loopmgr)),
|
2024-05-24 11:35:40 +02:00
|
|
|
.references = ISC_REFCOUNT_INITIALIZER(1),
|
|
|
|
.magic = CACHE_MAGIC,
|
2022-11-02 11:40:19 +01:00
|
|
|
};
|
2009-01-09 22:24:37 +00:00
|
|
|
|
2018-11-16 15:33:22 +01:00
|
|
|
isc_mutex_init(&cache->lock);
|
2024-03-27 11:32:25 +11:00
|
|
|
isc_mem_attach(mctx, &cache->mctx);
|
2008-02-07 23:46:54 +00:00
|
|
|
|
2023-06-26 10:58:30 +02:00
|
|
|
isc_stats_create(mctx, &cache->stats, dns_cachestatscounter_max);
|
2012-05-14 10:06:05 -07:00
|
|
|
|
2011-03-03 04:42:25 +00:00
|
|
|
/*
|
|
|
|
* Create the database
|
|
|
|
*/
|
2024-02-16 11:40:26 +11:00
|
|
|
result = cache_create_db(cache, &cache->db, &cache->tmctx,
|
|
|
|
&cache->hmctx);
|
2008-02-07 23:46:54 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
2024-05-24 11:35:40 +02:00
|
|
|
goto cleanup;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2012-05-14 10:06:05 -07:00
|
|
|
|
2008-02-07 23:46:54 +00:00
|
|
|
*cachep = cache;
|
|
|
|
return (ISC_R_SUCCESS);
|
2000-02-03 18:59:24 +00:00
|
|
|
|
2024-05-24 11:35:40 +02:00
|
|
|
cleanup:
|
|
|
|
cache_destroy(cache);
|
2008-02-07 23:46:54 +00:00
|
|
|
return (result);
|
1999-11-29 17:58:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2024-05-24 11:35:40 +02:00
|
|
|
cache_cleanup(dns_cache_t *cache) {
|
2008-02-07 23:46:54 +00:00
|
|
|
REQUIRE(VALID_CACHE(cache));
|
2019-08-26 14:19:45 +10:00
|
|
|
|
|
|
|
isc_refcount_destroy(&cache->references);
|
2024-05-24 11:35:40 +02:00
|
|
|
cache->magic = 0;
|
2000-08-31 12:15:17 +00:00
|
|
|
|
2024-02-16 11:40:26 +11:00
|
|
|
isc_mem_clearwater(cache->tmctx);
|
2022-11-02 11:40:19 +01:00
|
|
|
dns_db_detach(&cache->db);
|
1999-11-29 17:58:39 +00:00
|
|
|
|
2024-05-24 11:35:40 +02:00
|
|
|
cache_destroy(cache);
|
1999-11-29 17:58:39 +00:00
|
|
|
}
|
|
|
|
|
2024-05-24 15:42:41 +00:00
|
|
|
#if DNS_CACHE_TRACE
|
|
|
|
ISC_REFCOUNT_TRACE_IMPL(dns_cache, cache_cleanup);
|
|
|
|
#else
|
2024-05-24 11:35:40 +02:00
|
|
|
ISC_REFCOUNT_IMPL(dns_cache, cache_cleanup);
|
2024-05-24 15:42:41 +00:00
|
|
|
#endif
|
1999-11-29 17:58:39 +00:00
|
|
|
|
|
|
|
void
|
1999-12-02 22:35:29 +00:00
|
|
|
dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) {
|
2008-02-07 23:46:54 +00:00
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
REQUIRE(dbp != NULL && *dbp == NULL);
|
|
|
|
REQUIRE(cache->db != NULL);
|
2001-06-05 22:27:51 +00:00
|
|
|
|
2008-02-07 23:46:54 +00:00
|
|
|
LOCK(&cache->lock);
|
|
|
|
dns_db_attach(cache->db, dbp);
|
|
|
|
UNLOCK(&cache->lock);
|
1999-11-29 17:58:39 +00:00
|
|
|
}
|
|
|
|
|
2009-01-09 22:24:37 +00:00
|
|
|
const char *
|
|
|
|
dns_cache_getname(dns_cache_t *cache) {
|
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
|
|
|
|
return (cache->name);
|
|
|
|
}
|
|
|
|
|
2024-02-16 11:40:26 +11:00
|
|
|
static void
|
|
|
|
updatewater(dns_cache_t *cache) {
|
|
|
|
size_t hi = cache->size - (cache->size >> 3); /* ~ 7/8ths. */
|
|
|
|
size_t lo = cache->size - (cache->size >> 2); /* ~ 3/4ths. */
|
|
|
|
if (cache->size == 0U || hi == 0U || lo == 0U) {
|
|
|
|
isc_mem_clearwater(cache->tmctx);
|
|
|
|
} else {
|
|
|
|
isc_mem_setwater(cache->tmctx, hi, lo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-08-31 12:15:17 +00:00
|
|
|
void
|
2013-02-28 09:29:12 -08:00
|
|
|
dns_cache_setcachesize(dns_cache_t *cache, size_t size) {
|
2008-02-07 23:46:54 +00:00
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
|
|
|
|
/*
|
2009-01-17 14:18:27 +00:00
|
|
|
* Impose a minimum cache size; pathological things happen if there
|
2008-02-07 23:46:54 +00:00
|
|
|
* is too little room.
|
|
|
|
*/
|
2013-03-05 23:41:22 +11:00
|
|
|
if (size != 0U && size < DNS_CACHE_MINSIZE) {
|
2008-02-07 23:46:54 +00:00
|
|
|
size = DNS_CACHE_MINSIZE;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-02-07 23:46:54 +00:00
|
|
|
|
2009-01-09 22:24:37 +00:00
|
|
|
LOCK(&cache->lock);
|
|
|
|
cache->size = size;
|
2024-02-16 11:40:26 +11:00
|
|
|
updatewater(cache);
|
2009-01-09 22:24:37 +00:00
|
|
|
UNLOCK(&cache->lock);
|
2000-08-31 12:15:17 +00:00
|
|
|
}
|
|
|
|
|
2013-02-28 09:29:12 -08:00
|
|
|
size_t
|
2009-01-09 22:24:37 +00:00
|
|
|
dns_cache_getcachesize(dns_cache_t *cache) {
|
2013-02-28 09:29:12 -08:00
|
|
|
size_t size;
|
2009-01-09 22:24:37 +00:00
|
|
|
|
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
|
|
|
|
LOCK(&cache->lock);
|
|
|
|
size = cache->size;
|
|
|
|
UNLOCK(&cache->lock);
|
|
|
|
|
|
|
|
return (size);
|
|
|
|
}
|
|
|
|
|
2017-09-06 09:58:29 +10:00
|
|
|
void
|
|
|
|
dns_cache_setservestalettl(dns_cache_t *cache, dns_ttl_t ttl) {
|
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
|
|
|
|
LOCK(&cache->lock);
|
|
|
|
cache->serve_stale_ttl = ttl;
|
|
|
|
UNLOCK(&cache->lock);
|
|
|
|
|
|
|
|
(void)dns_db_setservestalettl(cache->db, ttl);
|
|
|
|
}
|
|
|
|
|
|
|
|
dns_ttl_t
|
|
|
|
dns_cache_getservestalettl(dns_cache_t *cache) {
|
|
|
|
dns_ttl_t ttl;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Could get it straight from the dns_cache_t, but use db
|
|
|
|
* to confirm the value that the db is really using.
|
|
|
|
*/
|
|
|
|
result = dns_db_getservestalettl(cache->db, &ttl);
|
|
|
|
return (result == ISC_R_SUCCESS ? ttl : 0);
|
|
|
|
}
|
|
|
|
|
Add stale-refresh-time option
Before this update, BIND would attempt to do a full recursive resolution
process for each query received if the requested rrset had its ttl
expired. If the resolution fails for any reason, only then BIND would
check for stale rrset in cache (if 'stale-cache-enable' and
'stale-answer-enable' is on).
The problem with this approach is that if an authoritative server is
unreachable or is failing to respond, it is very unlikely that the
problem will be fixed in the next seconds.
A better approach to improve performance in those cases, is to mark the
moment in which a resolution failed, and if new queries arrive for that
same rrset, try to respond directly from the stale cache, and do that
for a window of time configured via 'stale-refresh-time'.
Only when this interval expires we then try to do a normal refresh of
the rrset.
The logic behind this commit is as following:
- In query.c / query_gotanswer(), if the test of 'result' variable falls
to the default case, an error is assumed to have happened, and a call
to 'query_usestale()' is made to check if serving of stale rrset is
enabled in configuration.
- If serving of stale answers is enabled, a flag will be turned on in
the query context to look for stale records:
query.c:6839
qctx->client->query.dboptions |= DNS_DBFIND_STALEOK;
- A call to query_lookup() will be made again, inside it a call to
'dns_db_findext()' is made, which in turn will invoke rbdb.c /
cache_find().
- In rbtdb.c / cache_find() the important bits of this change is the
call to 'check_stale_header()', which is a function that yields true
if we should skip the stale entry, or false if we should consider it.
- In check_stale_header() we now check if the DNS_DBFIND_STALEOK option
is set, if that is the case we know that this new search for stale
records was made due to a failure in a normal resolution, so we keep
track of the time in which the failured occured in rbtdb.c:4559:
header->last_refresh_fail_ts = search->now;
- In check_stale_header(), if DNS_DBFIND_STALEOK is not set, then we
know this is a normal lookup, if the record is stale and the query
time is between last failure time + stale-refresh-time window, then
we return false so cache_find() knows it can consider this stale
rrset entry to return as a response.
The last additions are two new methods to the database interface:
- setservestale_refresh
- getservestale_refresh
Those were added so rbtdb can be aware of the value set in configuration
option, since in that level we have no access to the view object.
2020-10-19 17:02:03 -03:00
|
|
|
void
|
2020-11-10 13:50:54 -03:00
|
|
|
dns_cache_setservestalerefresh(dns_cache_t *cache, dns_ttl_t interval) {
|
Add stale-refresh-time option
Before this update, BIND would attempt to do a full recursive resolution
process for each query received if the requested rrset had its ttl
expired. If the resolution fails for any reason, only then BIND would
check for stale rrset in cache (if 'stale-cache-enable' and
'stale-answer-enable' is on).
The problem with this approach is that if an authoritative server is
unreachable or is failing to respond, it is very unlikely that the
problem will be fixed in the next seconds.
A better approach to improve performance in those cases, is to mark the
moment in which a resolution failed, and if new queries arrive for that
same rrset, try to respond directly from the stale cache, and do that
for a window of time configured via 'stale-refresh-time'.
Only when this interval expires we then try to do a normal refresh of
the rrset.
The logic behind this commit is as following:
- In query.c / query_gotanswer(), if the test of 'result' variable falls
to the default case, an error is assumed to have happened, and a call
to 'query_usestale()' is made to check if serving of stale rrset is
enabled in configuration.
- If serving of stale answers is enabled, a flag will be turned on in
the query context to look for stale records:
query.c:6839
qctx->client->query.dboptions |= DNS_DBFIND_STALEOK;
- A call to query_lookup() will be made again, inside it a call to
'dns_db_findext()' is made, which in turn will invoke rbdb.c /
cache_find().
- In rbtdb.c / cache_find() the important bits of this change is the
call to 'check_stale_header()', which is a function that yields true
if we should skip the stale entry, or false if we should consider it.
- In check_stale_header() we now check if the DNS_DBFIND_STALEOK option
is set, if that is the case we know that this new search for stale
records was made due to a failure in a normal resolution, so we keep
track of the time in which the failured occured in rbtdb.c:4559:
header->last_refresh_fail_ts = search->now;
- In check_stale_header(), if DNS_DBFIND_STALEOK is not set, then we
know this is a normal lookup, if the record is stale and the query
time is between last failure time + stale-refresh-time window, then
we return false so cache_find() knows it can consider this stale
rrset entry to return as a response.
The last additions are two new methods to the database interface:
- setservestale_refresh
- getservestale_refresh
Those were added so rbtdb can be aware of the value set in configuration
option, since in that level we have no access to the view object.
2020-10-19 17:02:03 -03:00
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
|
2020-11-10 13:50:54 -03:00
|
|
|
LOCK(&cache->lock);
|
|
|
|
cache->serve_stale_refresh = interval;
|
|
|
|
UNLOCK(&cache->lock);
|
|
|
|
|
Add stale-refresh-time option
Before this update, BIND would attempt to do a full recursive resolution
process for each query received if the requested rrset had its ttl
expired. If the resolution fails for any reason, only then BIND would
check for stale rrset in cache (if 'stale-cache-enable' and
'stale-answer-enable' is on).
The problem with this approach is that if an authoritative server is
unreachable or is failing to respond, it is very unlikely that the
problem will be fixed in the next seconds.
A better approach to improve performance in those cases, is to mark the
moment in which a resolution failed, and if new queries arrive for that
same rrset, try to respond directly from the stale cache, and do that
for a window of time configured via 'stale-refresh-time'.
Only when this interval expires we then try to do a normal refresh of
the rrset.
The logic behind this commit is as following:
- In query.c / query_gotanswer(), if the test of 'result' variable falls
to the default case, an error is assumed to have happened, and a call
to 'query_usestale()' is made to check if serving of stale rrset is
enabled in configuration.
- If serving of stale answers is enabled, a flag will be turned on in
the query context to look for stale records:
query.c:6839
qctx->client->query.dboptions |= DNS_DBFIND_STALEOK;
- A call to query_lookup() will be made again, inside it a call to
'dns_db_findext()' is made, which in turn will invoke rbdb.c /
cache_find().
- In rbtdb.c / cache_find() the important bits of this change is the
call to 'check_stale_header()', which is a function that yields true
if we should skip the stale entry, or false if we should consider it.
- In check_stale_header() we now check if the DNS_DBFIND_STALEOK option
is set, if that is the case we know that this new search for stale
records was made due to a failure in a normal resolution, so we keep
track of the time in which the failured occured in rbtdb.c:4559:
header->last_refresh_fail_ts = search->now;
- In check_stale_header(), if DNS_DBFIND_STALEOK is not set, then we
know this is a normal lookup, if the record is stale and the query
time is between last failure time + stale-refresh-time window, then
we return false so cache_find() knows it can consider this stale
rrset entry to return as a response.
The last additions are two new methods to the database interface:
- setservestale_refresh
- getservestale_refresh
Those were added so rbtdb can be aware of the value set in configuration
option, since in that level we have no access to the view object.
2020-10-19 17:02:03 -03:00
|
|
|
(void)dns_db_setservestalerefresh(cache->db, interval);
|
|
|
|
}
|
|
|
|
|
2020-11-10 13:50:54 -03:00
|
|
|
dns_ttl_t
|
|
|
|
dns_cache_getservestalerefresh(dns_cache_t *cache) {
|
|
|
|
isc_result_t result;
|
|
|
|
dns_ttl_t interval;
|
|
|
|
|
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
|
|
|
|
result = dns_db_getservestalerefresh(cache->db, &interval);
|
|
|
|
return (result == ISC_R_SUCCESS ? interval : 0);
|
|
|
|
}
|
|
|
|
|
2001-04-11 20:37:50 +00:00
|
|
|
isc_result_t
|
|
|
|
dns_cache_flush(dns_cache_t *cache) {
|
2015-07-03 10:17:33 +10:00
|
|
|
dns_db_t *db = NULL, *olddb;
|
2024-02-16 11:40:26 +11:00
|
|
|
isc_mem_t *tmctx = NULL, *oldtmctx;
|
|
|
|
isc_mem_t *hmctx = NULL, *oldhmctx;
|
2008-02-07 23:46:54 +00:00
|
|
|
isc_result_t result;
|
|
|
|
|
2024-02-16 11:40:26 +11:00
|
|
|
result = cache_create_db(cache, &db, &tmctx, &hmctx);
|
2008-02-07 23:46:54 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
return (result);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-02-07 23:46:54 +00:00
|
|
|
|
|
|
|
LOCK(&cache->lock);
|
2024-02-16 11:40:26 +11:00
|
|
|
isc_mem_clearwater(cache->tmctx);
|
|
|
|
oldhmctx = cache->hmctx;
|
|
|
|
cache->hmctx = hmctx;
|
|
|
|
oldtmctx = cache->tmctx;
|
|
|
|
cache->tmctx = tmctx;
|
|
|
|
updatewater(cache);
|
2015-07-03 10:17:33 +10:00
|
|
|
olddb = cache->db;
|
2008-02-07 23:46:54 +00:00
|
|
|
cache->db = db;
|
|
|
|
UNLOCK(&cache->lock);
|
|
|
|
|
2015-07-03 10:17:33 +10:00
|
|
|
dns_db_detach(&olddb);
|
2024-02-16 11:40:26 +11:00
|
|
|
isc_mem_detach(&oldhmctx);
|
|
|
|
isc_mem_detach(&oldtmctx);
|
2015-07-03 10:17:33 +10:00
|
|
|
|
2008-02-07 23:46:54 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
2001-04-11 20:37:50 +00:00
|
|
|
}
|
2001-11-27 03:10:32 +00:00
|
|
|
|
2011-08-02 20:36:13 +00:00
|
|
|
static isc_result_t
|
|
|
|
clearnode(dns_db_t *db, dns_dbnode_t *node) {
|
2008-02-07 23:46:54 +00:00
|
|
|
isc_result_t result;
|
|
|
|
dns_rdatasetiter_t *iter = NULL;
|
|
|
|
|
2022-11-16 11:40:33 +11:00
|
|
|
result = dns_db_allrdatasets(db, node, NULL, DNS_DB_STALEOK,
|
|
|
|
(isc_stdtime_t)0, &iter);
|
2008-02-07 23:46:54 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
2011-08-02 20:36:13 +00:00
|
|
|
return (result);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-02-07 23:46:54 +00:00
|
|
|
|
|
|
|
for (result = dns_rdatasetiter_first(iter); result == ISC_R_SUCCESS;
|
|
|
|
result = dns_rdatasetiter_next(iter))
|
|
|
|
{
|
|
|
|
dns_rdataset_t rdataset;
|
|
|
|
dns_rdataset_init(&rdataset);
|
|
|
|
|
|
|
|
dns_rdatasetiter_current(iter, &rdataset);
|
2011-08-02 20:36:13 +00:00
|
|
|
result = dns_db_deleterdataset(db, node, NULL, rdataset.type,
|
2008-02-07 23:46:54 +00:00
|
|
|
rdataset.covers);
|
|
|
|
dns_rdataset_disassociate(&rdataset);
|
|
|
|
if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) {
|
|
|
|
break;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-02-07 23:46:54 +00:00
|
|
|
}
|
2011-08-02 20:36:13 +00:00
|
|
|
|
2008-02-07 23:46:54 +00:00
|
|
|
if (result == ISC_R_NOMORE) {
|
|
|
|
result = ISC_R_SUCCESS;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2008-02-07 23:46:54 +00:00
|
|
|
|
|
|
|
dns_rdatasetiter_destroy(&iter);
|
2011-08-02 20:36:13 +00:00
|
|
|
return (result);
|
|
|
|
}
|
2001-11-27 03:10:32 +00:00
|
|
|
|
2011-08-02 20:36:13 +00:00
|
|
|
static isc_result_t
|
2016-12-30 15:45:08 +11:00
|
|
|
cleartree(dns_db_t *db, const dns_name_t *name) {
|
2011-08-26 05:12:56 +00:00
|
|
|
isc_result_t result, answer = ISC_R_SUCCESS;
|
2011-08-02 20:36:13 +00:00
|
|
|
dns_dbiterator_t *iter = NULL;
|
2016-03-27 08:25:44 +11:00
|
|
|
dns_dbnode_t *node = NULL, *top = NULL;
|
2011-08-02 20:36:13 +00:00
|
|
|
dns_fixedname_t fnodename;
|
|
|
|
dns_name_t *nodename;
|
|
|
|
|
2016-03-27 08:25:44 +11:00
|
|
|
/*
|
|
|
|
* Create the node if it doesn't exist so dns_dbiterator_seek()
|
|
|
|
* can find it. We will continue even if this fails.
|
|
|
|
*/
|
2018-04-17 08:29:14 -07:00
|
|
|
(void)dns_db_findnode(db, name, true, &top);
|
2016-03-27 08:25:44 +11:00
|
|
|
|
2018-03-28 14:38:09 +02:00
|
|
|
nodename = dns_fixedname_initname(&fnodename);
|
2011-08-02 20:36:13 +00:00
|
|
|
|
|
|
|
result = dns_db_createiterator(db, 0, &iter);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
goto cleanup;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-02 20:36:13 +00:00
|
|
|
|
|
|
|
result = dns_dbiterator_seek(iter, name);
|
2016-03-24 11:31:25 +11:00
|
|
|
if (result == DNS_R_PARTIALMATCH) {
|
|
|
|
result = dns_dbiterator_next(iter);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-02 20:36:13 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
goto cleanup;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-02 20:36:13 +00:00
|
|
|
|
|
|
|
while (result == ISC_R_SUCCESS) {
|
|
|
|
result = dns_dbiterator_current(iter, &node, nodename);
|
2011-08-26 05:12:56 +00:00
|
|
|
if (result == DNS_R_NEWORIGIN) {
|
|
|
|
result = ISC_R_SUCCESS;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-26 05:12:56 +00:00
|
|
|
if (result != ISC_R_SUCCESS) {
|
2011-08-02 20:36:13 +00:00
|
|
|
goto cleanup;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-26 05:12:56 +00:00
|
|
|
/*
|
|
|
|
* Are we done?
|
|
|
|
*/
|
2011-08-02 20:36:13 +00:00
|
|
|
if (!dns_name_issubdomain(nodename, name)) {
|
|
|
|
goto cleanup;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-02 20:36:13 +00:00
|
|
|
|
2011-08-26 05:12:56 +00:00
|
|
|
/*
|
|
|
|
* If clearnode fails record and move onto the next node.
|
|
|
|
*/
|
2011-08-02 20:36:13 +00:00
|
|
|
result = clearnode(db, node);
|
2011-08-26 05:12:56 +00:00
|
|
|
if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) {
|
|
|
|
answer = result;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-02 20:36:13 +00:00
|
|
|
dns_db_detachnode(db, &node);
|
|
|
|
result = dns_dbiterator_next(iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (result == ISC_R_NOMORE || result == ISC_R_NOTFOUND) {
|
|
|
|
result = ISC_R_SUCCESS;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-26 05:12:56 +00:00
|
|
|
if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) {
|
|
|
|
answer = result;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-02 20:36:13 +00:00
|
|
|
if (node != NULL) {
|
|
|
|
dns_db_detachnode(db, &node);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-02 20:36:13 +00:00
|
|
|
if (iter != NULL) {
|
|
|
|
dns_dbiterator_destroy(&iter);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2016-03-27 08:25:44 +11:00
|
|
|
if (top != NULL) {
|
|
|
|
dns_db_detachnode(db, &top);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-02 20:36:13 +00:00
|
|
|
|
2011-08-26 05:12:56 +00:00
|
|
|
return (answer);
|
2011-08-02 20:36:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
isc_result_t
|
2016-12-30 15:45:08 +11:00
|
|
|
dns_cache_flushname(dns_cache_t *cache, const dns_name_t *name) {
|
2018-04-17 08:29:14 -07:00
|
|
|
return (dns_cache_flushnode(cache, name, false));
|
2011-08-02 20:36:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
isc_result_t
|
2016-12-30 15:45:08 +11:00
|
|
|
dns_cache_flushnode(dns_cache_t *cache, const dns_name_t *name, bool tree) {
|
2011-08-02 20:36:13 +00:00
|
|
|
isc_result_t result;
|
|
|
|
dns_dbnode_t *node = NULL;
|
|
|
|
dns_db_t *db = NULL;
|
|
|
|
|
2016-03-24 11:31:25 +11:00
|
|
|
if (tree && dns_name_equal(name, dns_rootname)) {
|
2011-08-02 20:36:13 +00:00
|
|
|
return (dns_cache_flush(cache));
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-02 20:36:13 +00:00
|
|
|
|
|
|
|
LOCK(&cache->lock);
|
|
|
|
if (cache->db != NULL) {
|
|
|
|
dns_db_attach(cache->db, &db);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-02 20:36:13 +00:00
|
|
|
UNLOCK(&cache->lock);
|
|
|
|
if (db == NULL) {
|
|
|
|
return (ISC_R_SUCCESS);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-02 20:36:13 +00:00
|
|
|
|
|
|
|
if (tree) {
|
|
|
|
result = cleartree(cache->db, name);
|
|
|
|
} else {
|
2018-04-17 08:29:14 -07:00
|
|
|
result = dns_db_findnode(cache->db, name, false, &node);
|
2011-08-02 20:36:13 +00:00
|
|
|
if (result == ISC_R_NOTFOUND) {
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
goto cleanup_db;
|
|
|
|
}
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
goto cleanup_db;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2011-08-02 20:36:13 +00:00
|
|
|
result = clearnode(cache->db, node);
|
|
|
|
dns_db_detachnode(cache->db, &node);
|
|
|
|
}
|
2001-11-27 03:10:32 +00:00
|
|
|
|
|
|
|
cleanup_db:
|
2008-02-07 23:46:54 +00:00
|
|
|
dns_db_detach(&db);
|
|
|
|
return (result);
|
2007-10-19 17:15:53 +00:00
|
|
|
}
|
2012-05-14 10:06:05 -07:00
|
|
|
|
|
|
|
isc_stats_t *
|
|
|
|
dns_cache_getstats(dns_cache_t *cache) {
|
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
return (cache->stats);
|
|
|
|
}
|
|
|
|
|
2013-07-25 10:51:31 -07:00
|
|
|
void
|
|
|
|
dns_cache_updatestats(dns_cache_t *cache, isc_result_t result) {
|
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
if (cache->stats == NULL) {
|
|
|
|
return;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2013-07-25 10:51:31 -07:00
|
|
|
|
|
|
|
switch (result) {
|
|
|
|
case ISC_R_SUCCESS:
|
|
|
|
case DNS_R_NCACHENXDOMAIN:
|
|
|
|
case DNS_R_NCACHENXRRSET:
|
|
|
|
case DNS_R_CNAME:
|
|
|
|
case DNS_R_DNAME:
|
|
|
|
case DNS_R_GLUE:
|
|
|
|
case DNS_R_ZONECUT:
|
2021-10-15 14:47:07 +11:00
|
|
|
case DNS_R_COVERINGNSEC:
|
2013-07-25 10:51:31 -07:00
|
|
|
isc_stats_increment(cache->stats,
|
|
|
|
dns_cachestatscounter_queryhits);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
isc_stats_increment(cache->stats,
|
|
|
|
dns_cachestatscounter_querymisses);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-14 10:06:05 -07:00
|
|
|
/*
|
|
|
|
* XXX: Much of the following code has been copied in from statschannel.c.
|
|
|
|
* We should refactor this into a generic function in stats.c that can be
|
|
|
|
* called from both places.
|
|
|
|
*/
|
|
|
|
typedef struct cache_dumparg {
|
|
|
|
isc_statsformat_t type;
|
|
|
|
void *arg; /* type dependent argument */
|
|
|
|
int ncounters; /* for general statistics */
|
|
|
|
int *counterindices; /* for general statistics */
|
2018-03-28 14:19:37 +02:00
|
|
|
uint64_t *countervalues; /* for general statistics */
|
2012-05-14 10:06:05 -07:00
|
|
|
isc_result_t result;
|
|
|
|
} cache_dumparg_t;
|
|
|
|
|
|
|
|
static void
|
2018-03-28 14:19:37 +02:00
|
|
|
getcounter(isc_statscounter_t counter, uint64_t val, void *arg) {
|
2012-05-14 10:06:05 -07:00
|
|
|
cache_dumparg_t *dumparg = arg;
|
|
|
|
|
|
|
|
REQUIRE(counter < dumparg->ncounters);
|
|
|
|
dumparg->countervalues[counter] = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
getcounters(isc_stats_t *stats, isc_statsformat_t type, int ncounters,
|
2018-03-28 14:19:37 +02:00
|
|
|
int *indices, uint64_t *values) {
|
2012-05-14 10:06:05 -07:00
|
|
|
cache_dumparg_t dumparg;
|
|
|
|
|
|
|
|
memset(values, 0, sizeof(values[0]) * ncounters);
|
|
|
|
|
|
|
|
dumparg.type = type;
|
|
|
|
dumparg.ncounters = ncounters;
|
|
|
|
dumparg.counterindices = indices;
|
|
|
|
dumparg.countervalues = values;
|
|
|
|
|
|
|
|
isc_stats_dump(stats, getcounter, &dumparg, ISC_STATSDUMP_VERBOSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dns_cache_dumpstats(dns_cache_t *cache, FILE *fp) {
|
|
|
|
int indices[dns_cachestatscounter_max];
|
2018-03-28 14:19:37 +02:00
|
|
|
uint64_t values[dns_cachestatscounter_max];
|
2012-05-14 10:06:05 -07:00
|
|
|
|
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
|
|
|
|
getcounters(cache->stats, isc_statsformat_file,
|
|
|
|
dns_cachestatscounter_max, indices, values);
|
|
|
|
|
|
|
|
fprintf(fp, "%20" PRIu64 " %s\n", values[dns_cachestatscounter_hits],
|
|
|
|
"cache hits");
|
|
|
|
fprintf(fp, "%20" PRIu64 " %s\n", values[dns_cachestatscounter_misses],
|
|
|
|
"cache misses");
|
2018-03-28 14:56:40 +02:00
|
|
|
fprintf(fp, "%20" PRIu64 " %s\n",
|
2012-05-14 10:06:05 -07:00
|
|
|
values[dns_cachestatscounter_queryhits],
|
|
|
|
"cache hits (from query)");
|
2018-03-28 14:56:40 +02:00
|
|
|
fprintf(fp, "%20" PRIu64 " %s\n",
|
2012-05-14 10:06:05 -07:00
|
|
|
values[dns_cachestatscounter_querymisses],
|
|
|
|
"cache misses (from query)");
|
2018-03-28 14:56:40 +02:00
|
|
|
fprintf(fp, "%20" PRIu64 " %s\n",
|
2012-05-14 10:06:05 -07:00
|
|
|
values[dns_cachestatscounter_deletelru],
|
|
|
|
"cache records deleted due to memory exhaustion");
|
2018-03-28 14:56:40 +02:00
|
|
|
fprintf(fp, "%20" PRIu64 " %s\n",
|
2012-05-14 10:06:05 -07:00
|
|
|
values[dns_cachestatscounter_deletettl],
|
|
|
|
"cache records deleted due to TTL expiration");
|
2021-10-27 13:25:41 +11:00
|
|
|
fprintf(fp, "%20" PRIu64 " %s\n",
|
|
|
|
values[dns_cachestatscounter_coveringnsec],
|
|
|
|
"covering nsec returned");
|
2021-10-20 12:01:00 +11:00
|
|
|
fprintf(fp, "%20u %s\n", dns_db_nodecount(cache->db, dns_dbtree_main),
|
2012-05-14 10:06:05 -07:00
|
|
|
"cache database nodes");
|
2021-10-20 12:44:59 +11:00
|
|
|
fprintf(fp, "%20u %s\n", dns_db_nodecount(cache->db, dns_dbtree_nsec),
|
|
|
|
"cache NSEC auxiliary database nodes");
|
2018-03-28 14:19:37 +02:00
|
|
|
fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)dns_db_hashsize(cache->db),
|
2012-05-14 10:06:05 -07:00
|
|
|
"cache database hash buckets");
|
|
|
|
|
2024-02-16 11:40:26 +11:00
|
|
|
fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_inuse(cache->tmctx),
|
2012-05-14 10:06:05 -07:00
|
|
|
"cache tree memory in use");
|
|
|
|
|
2018-03-28 14:19:37 +02:00
|
|
|
fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_inuse(cache->hmctx),
|
2012-05-14 10:06:05 -07:00
|
|
|
"cache heap memory in use");
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBXML2
|
2012-11-01 10:22:11 +11:00
|
|
|
#define TRY0(a) \
|
|
|
|
do { \
|
|
|
|
xmlrc = (a); \
|
|
|
|
if (xmlrc < 0) \
|
|
|
|
goto error; \
|
|
|
|
} while (0)
|
|
|
|
static int
|
2018-03-28 14:19:37 +02:00
|
|
|
renderstat(const char *name, uint64_t value, xmlTextWriterPtr writer) {
|
2012-11-01 10:22:11 +11:00
|
|
|
int xmlrc;
|
|
|
|
|
2013-02-08 14:53:14 -08:00
|
|
|
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
|
|
|
|
TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
|
|
|
|
ISC_XMLCHAR name));
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", value));
|
2013-02-08 14:53:14 -08:00
|
|
|
TRY0(xmlTextWriterEndElement(writer)); /* counter */
|
|
|
|
|
2012-11-01 10:22:11 +11:00
|
|
|
error:
|
|
|
|
return (xmlrc);
|
2012-05-14 10:06:05 -07:00
|
|
|
}
|
|
|
|
|
2012-11-01 10:22:11 +11:00
|
|
|
int
|
2019-06-24 14:25:55 +02:00
|
|
|
dns_cache_renderxml(dns_cache_t *cache, void *writer0) {
|
2012-05-14 10:06:05 -07:00
|
|
|
int indices[dns_cachestatscounter_max];
|
2018-03-28 14:19:37 +02:00
|
|
|
uint64_t values[dns_cachestatscounter_max];
|
2012-11-01 10:22:11 +11:00
|
|
|
int xmlrc;
|
2019-06-24 14:25:55 +02:00
|
|
|
xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0;
|
2012-05-14 10:06:05 -07:00
|
|
|
|
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
|
|
|
|
getcounters(cache->stats, isc_statsformat_file,
|
|
|
|
dns_cachestatscounter_max, indices, values);
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(renderstat("CacheHits", values[dns_cachestatscounter_hits],
|
|
|
|
writer));
|
|
|
|
TRY0(renderstat("CacheMisses", values[dns_cachestatscounter_misses],
|
|
|
|
writer));
|
|
|
|
TRY0(renderstat("QueryHits", values[dns_cachestatscounter_queryhits],
|
|
|
|
writer));
|
|
|
|
TRY0(renderstat("QueryMisses",
|
|
|
|
values[dns_cachestatscounter_querymisses], writer));
|
|
|
|
TRY0(renderstat("DeleteLRU", values[dns_cachestatscounter_deletelru],
|
|
|
|
writer));
|
|
|
|
TRY0(renderstat("DeleteTTL", values[dns_cachestatscounter_deletettl],
|
|
|
|
writer));
|
2021-10-27 13:25:41 +11:00
|
|
|
TRY0(renderstat("CoveringNSEC",
|
|
|
|
values[dns_cachestatscounter_coveringnsec], writer));
|
2012-11-01 10:22:11 +11:00
|
|
|
|
2021-10-20 12:01:00 +11:00
|
|
|
TRY0(renderstat("CacheNodes",
|
|
|
|
dns_db_nodecount(cache->db, dns_dbtree_main), writer));
|
2021-10-20 12:44:59 +11:00
|
|
|
TRY0(renderstat("CacheNSECNodes",
|
|
|
|
dns_db_nodecount(cache->db, dns_dbtree_nsec), writer));
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(renderstat("CacheBuckets", dns_db_hashsize(cache->db), writer));
|
|
|
|
|
2024-02-16 11:40:26 +11:00
|
|
|
TRY0(renderstat("TreeMemInUse", isc_mem_inuse(cache->tmctx), writer));
|
2012-11-01 10:22:11 +11:00
|
|
|
|
|
|
|
TRY0(renderstat("HeapMemInUse", isc_mem_inuse(cache->hmctx), writer));
|
|
|
|
error:
|
|
|
|
return (xmlrc);
|
2012-05-14 10:06:05 -07:00
|
|
|
}
|
|
|
|
#endif /* ifdef HAVE_LIBXML2 */
|
2013-03-13 14:24:50 -07:00
|
|
|
|
2019-02-06 11:56:42 +01:00
|
|
|
#ifdef HAVE_JSON_C
|
2013-03-13 14:24:50 -07:00
|
|
|
#define CHECKMEM(m) \
|
|
|
|
do { \
|
|
|
|
if (m == NULL) { \
|
|
|
|
result = ISC_R_NOMEMORY; \
|
|
|
|
goto error; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
isc_result_t
|
2019-06-24 12:21:47 +02:00
|
|
|
dns_cache_renderjson(dns_cache_t *cache, void *cstats0) {
|
2013-03-13 14:24:50 -07:00
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
|
|
int indices[dns_cachestatscounter_max];
|
2018-03-28 14:19:37 +02:00
|
|
|
uint64_t values[dns_cachestatscounter_max];
|
2013-03-13 14:24:50 -07:00
|
|
|
json_object *obj;
|
2019-06-24 12:21:47 +02:00
|
|
|
json_object *cstats = (json_object *)cstats0;
|
2013-03-13 14:24:50 -07:00
|
|
|
|
|
|
|
REQUIRE(VALID_CACHE(cache));
|
|
|
|
|
|
|
|
getcounters(cache->stats, isc_statsformat_file,
|
|
|
|
dns_cachestatscounter_max, indices, values);
|
|
|
|
|
|
|
|
obj = json_object_new_int64(values[dns_cachestatscounter_hits]);
|
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(cstats, "CacheHits", obj);
|
|
|
|
|
|
|
|
obj = json_object_new_int64(values[dns_cachestatscounter_misses]);
|
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(cstats, "CacheMisses", obj);
|
|
|
|
|
|
|
|
obj = json_object_new_int64(values[dns_cachestatscounter_queryhits]);
|
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(cstats, "QueryHits", obj);
|
|
|
|
|
|
|
|
obj = json_object_new_int64(values[dns_cachestatscounter_querymisses]);
|
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(cstats, "QueryMisses", obj);
|
|
|
|
|
|
|
|
obj = json_object_new_int64(values[dns_cachestatscounter_deletelru]);
|
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(cstats, "DeleteLRU", obj);
|
|
|
|
|
|
|
|
obj = json_object_new_int64(values[dns_cachestatscounter_deletettl]);
|
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(cstats, "DeleteTTL", obj);
|
|
|
|
|
2021-10-27 13:25:41 +11:00
|
|
|
obj = json_object_new_int64(values[dns_cachestatscounter_coveringnsec]);
|
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(cstats, "CoveringNSEC", obj);
|
|
|
|
|
2021-10-20 12:01:00 +11:00
|
|
|
obj = json_object_new_int64(
|
|
|
|
dns_db_nodecount(cache->db, dns_dbtree_main));
|
2013-03-13 14:24:50 -07:00
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(cstats, "CacheNodes", obj);
|
|
|
|
|
2021-10-20 12:44:59 +11:00
|
|
|
obj = json_object_new_int64(
|
|
|
|
dns_db_nodecount(cache->db, dns_dbtree_nsec));
|
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(cstats, "CacheNSECNodes", obj);
|
|
|
|
|
2013-03-13 14:24:50 -07:00
|
|
|
obj = json_object_new_int64(dns_db_hashsize(cache->db));
|
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(cstats, "CacheBuckets", obj);
|
|
|
|
|
2024-02-16 11:40:26 +11:00
|
|
|
obj = json_object_new_int64(isc_mem_inuse(cache->tmctx));
|
2013-03-13 14:24:50 -07:00
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(cstats, "TreeMemInUse", obj);
|
|
|
|
|
|
|
|
obj = json_object_new_int64(isc_mem_inuse(cache->hmctx));
|
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(cstats, "HeapMemInUse", obj);
|
|
|
|
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
error:
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
#endif /* ifdef HAVE_JSON_C */
|