1/*	$NetBSD: cache.c,v 1.11 2024/02/21 22:52:05 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16/*! \file */
17
18#include <inttypes.h>
19#include <stdbool.h>
20
21#include <isc/mem.h>
22#include <isc/print.h>
23#include <isc/refcount.h>
24#include <isc/result.h>
25#include <isc/stats.h>
26#include <isc/string.h>
27#include <isc/task.h>
28#include <isc/time.h>
29#include <isc/timer.h>
30#include <isc/util.h>
31
32#include <dns/cache.h>
33#include <dns/db.h>
34#include <dns/dbiterator.h>
35#include <dns/events.h>
36#include <dns/log.h>
37#include <dns/masterdump.h>
38#include <dns/rdata.h>
39#include <dns/rdataset.h>
40#include <dns/rdatasetiter.h>
41#include <dns/stats.h>
42
43#ifdef HAVE_JSON_C
44#include <json_object.h>
45#endif /* HAVE_JSON_C */
46
47#ifdef HAVE_LIBXML2
48#include <libxml/xmlwriter.h>
49#define ISC_XMLCHAR (const xmlChar *)
50#endif /* HAVE_LIBXML2 */
51
52#include "rbtdb.h"
53
54#define CACHE_MAGIC	   ISC_MAGIC('$', '$', '$', '$')
55#define VALID_CACHE(cache) ISC_MAGIC_VALID(cache, CACHE_MAGIC)
56
57/*!
58 * Control incremental cleaning.
59 * DNS_CACHE_MINSIZE is how many bytes is the floor for
60 * dns_cache_setcachesize(). See also DNS_CACHE_CLEANERINCREMENT
61 */
62#define DNS_CACHE_MINSIZE 2097152U /*%< Bytes.  2097152 = 2 MB */
63/*!
64 * Control incremental cleaning.
65 * CLEANERINCREMENT is how many nodes are examined in one pass.
66 * See also DNS_CACHE_MINSIZE
67 */
68#define DNS_CACHE_CLEANERINCREMENT 1000U /*%< Number of nodes. */
69
70/***
71 ***	Types
72 ***/
73
74/*
75 * A cache_cleaner_t encapsulates the state of the periodic
76 * cache cleaning.
77 */
78
79typedef struct cache_cleaner cache_cleaner_t;
80
81typedef enum {
82	cleaner_s_idle, /*%< Waiting for cleaning interval to expire. */
83	cleaner_s_busy, /*%< Currently cleaning. */
84	cleaner_s_done	/*%< Freed enough memory after being overmem. */
85} cleaner_state_t;
86
87/*
88 * Convenience macros for comprehensive assertion checking.
89 */
90#define CLEANER_IDLE(c) \
91	((c)->state == cleaner_s_idle && (c)->resched_event != NULL)
92#define CLEANER_BUSY(c)                                           \
93	((c)->state == cleaner_s_busy && (c)->iterator != NULL && \
94	 (c)->resched_event == NULL)
95
96/*%
97 * Accesses to a cache cleaner object are synchronized through
98 * task/event serialization, or locked from the cache object.
99 */
100struct cache_cleaner {
101	isc_mutex_t lock;
102	/*%<
103	 * Locks overmem_event, overmem.  Note: never allocate memory
104	 * while holding this lock - that could lead to deadlock since
105	 * the lock is take by water() which is called from the memory
106	 * allocator.
107	 */
108
109	dns_cache_t *cache;
110	isc_task_t *task;
111	isc_event_t *resched_event; /*% Sent by cleaner task to
112				     * itself to reschedule */
113	isc_event_t *overmem_event;
114
115	dns_dbiterator_t *iterator;
116	unsigned int increment; /*% Number of names to
117				 * clean in one increment */
118	cleaner_state_t state;	/*% Idle/Busy. */
119	bool overmem;		/*% The cache is in an overmem state.
120				 * */
121	bool replaceiterator;
122};
123
124/*%
125 * The actual cache object.
126 */
127
128struct dns_cache {
129	/* Unlocked. */
130	unsigned int magic;
131	isc_mutex_t lock;
132	isc_mem_t *mctx;  /* Main cache memory */
133	isc_mem_t *hmctx; /* Heap memory */
134	char *name;
135	isc_refcount_t references;
136	isc_refcount_t live_tasks;
137
138	/* Locked by 'lock'. */
139	dns_rdataclass_t rdclass;
140	dns_db_t *db;
141	cache_cleaner_t cleaner;
142	char *db_type;
143	int db_argc;
144	char **db_argv;
145	size_t size;
146	dns_ttl_t serve_stale_ttl;
147	dns_ttl_t serve_stale_refresh;
148	isc_stats_t *stats;
149};
150
151/***
152 ***	Functions
153 ***/
154
155static isc_result_t
156cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
157		   isc_timermgr_t *timermgr, cache_cleaner_t *cleaner);
158
159static void
160incremental_cleaning_action(isc_task_t *task, isc_event_t *event);
161
162static void
163cleaner_shutdown_action(isc_task_t *task, isc_event_t *event);
164
165static void
166overmem_cleaning_action(isc_task_t *task, isc_event_t *event);
167
168static void
169water(void *arg, int mark);
170
171static isc_result_t
172cache_create_db(dns_cache_t *cache, dns_db_t **db) {
173	isc_result_t result;
174	result = dns_db_create(cache->mctx, cache->db_type, dns_rootname,
175			       dns_dbtype_cache, cache->rdclass, cache->db_argc,
176			       cache->db_argv, db);
177	if (result == ISC_R_SUCCESS) {
178		dns_db_setservestalettl(*db, cache->serve_stale_ttl);
179		dns_db_setservestalerefresh(*db, cache->serve_stale_refresh);
180	}
181	return (result);
182}
183
184isc_result_t
185dns_cache_create(isc_mem_t *cmctx, isc_mem_t *hmctx, isc_taskmgr_t *taskmgr,
186		 isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
187		 const char *cachename, const char *db_type,
188		 unsigned int db_argc, char **db_argv, dns_cache_t **cachep) {
189	isc_result_t result;
190	dns_cache_t *cache;
191	int i, extra = 0;
192	isc_task_t *dbtask;
193
194	REQUIRE(cachep != NULL);
195	REQUIRE(*cachep == NULL);
196	REQUIRE(cmctx != NULL);
197	REQUIRE(hmctx != NULL);
198	REQUIRE(cachename != NULL);
199
200	cache = isc_mem_get(cmctx, sizeof(*cache));
201
202	cache->mctx = cache->hmctx = NULL;
203	isc_mem_attach(cmctx, &cache->mctx);
204	isc_mem_attach(hmctx, &cache->hmctx);
205
206	cache->name = NULL;
207	if (cachename != NULL) {
208		cache->name = isc_mem_strdup(cmctx, cachename);
209	}
210
211	isc_mutex_init(&cache->lock);
212
213	isc_refcount_init(&cache->references, 1);
214	isc_refcount_init(&cache->live_tasks, 1);
215	cache->rdclass = rdclass;
216	cache->serve_stale_ttl = 0;
217
218	cache->stats = NULL;
219	result = isc_stats_create(cmctx, &cache->stats,
220				  dns_cachestatscounter_max);
221	if (result != ISC_R_SUCCESS) {
222		goto cleanup_lock;
223	}
224
225	cache->db_type = isc_mem_strdup(cmctx, db_type);
226
227	/*
228	 * For databases of type "rbt" we pass hmctx to dns_db_create()
229	 * via cache->db_argv, followed by the rest of the arguments in
230	 * db_argv (of which there really shouldn't be any).
231	 */
232	if (strcmp(cache->db_type, "rbt") == 0) {
233		extra = 1;
234	}
235
236	cache->db_argc = db_argc + extra;
237	cache->db_argv = NULL;
238
239	if (cache->db_argc != 0) {
240		cache->db_argv = isc_mem_get(cmctx,
241					     cache->db_argc * sizeof(char *));
242
243		for (i = 0; i < cache->db_argc; i++) {
244			cache->db_argv[i] = NULL;
245		}
246
247		cache->db_argv[0] = (char *)hmctx;
248		for (i = extra; i < cache->db_argc; i++) {
249			cache->db_argv[i] = isc_mem_strdup(cmctx,
250							   db_argv[i - extra]);
251		}
252	}
253
254	/*
255	 * Create the database
256	 */
257	cache->db = NULL;
258	result = cache_create_db(cache, &cache->db);
259	if (result != ISC_R_SUCCESS) {
260		goto cleanup_dbargv;
261	}
262	if (taskmgr != NULL) {
263		dbtask = NULL;
264		result = isc_task_create(taskmgr, 1, &dbtask);
265		if (result != ISC_R_SUCCESS) {
266			goto cleanup_db;
267		}
268
269		isc_task_setname(dbtask, "cache_dbtask", NULL);
270		dns_db_settask(cache->db, dbtask);
271		isc_task_detach(&dbtask);
272	}
273
274	cache->magic = CACHE_MAGIC;
275
276	/*
277	 * RBT-type cache DB has its own mechanism of cache cleaning and doesn't
278	 * need the control of the generic cleaner.
279	 */
280	if (strcmp(db_type, "rbt") == 0) {
281		result = cache_cleaner_init(cache, NULL, NULL, &cache->cleaner);
282	} else {
283		result = cache_cleaner_init(cache, taskmgr, timermgr,
284					    &cache->cleaner);
285	}
286	if (result != ISC_R_SUCCESS) {
287		goto cleanup_db;
288	}
289
290	result = dns_db_setcachestats(cache->db, cache->stats);
291	if (result != ISC_R_SUCCESS) {
292		goto cleanup_db;
293	}
294
295	*cachep = cache;
296	return (ISC_R_SUCCESS);
297
298cleanup_db:
299	dns_db_detach(&cache->db);
300cleanup_dbargv:
301	for (i = extra; i < cache->db_argc; i++) {
302		if (cache->db_argv[i] != NULL) {
303			isc_mem_free(cmctx, cache->db_argv[i]);
304		}
305	}
306	if (cache->db_argv != NULL) {
307		isc_mem_put(cmctx, cache->db_argv,
308			    cache->db_argc * sizeof(char *));
309	}
310	isc_mem_free(cmctx, cache->db_type);
311	isc_stats_detach(&cache->stats);
312cleanup_lock:
313	isc_mutex_destroy(&cache->lock);
314	if (cache->name != NULL) {
315		isc_mem_free(cmctx, cache->name);
316	}
317	isc_mem_detach(&cache->hmctx);
318	isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
319	return (result);
320}
321
322static void
323cache_free(dns_cache_t *cache) {
324	REQUIRE(VALID_CACHE(cache));
325
326	isc_refcount_destroy(&cache->references);
327	isc_refcount_destroy(&cache->live_tasks);
328
329	isc_mem_clearwater(cache->mctx);
330
331	if (cache->cleaner.task != NULL) {
332		isc_task_detach(&cache->cleaner.task);
333	}
334
335	if (cache->cleaner.overmem_event != NULL) {
336		isc_event_free(&cache->cleaner.overmem_event);
337	}
338
339	if (cache->cleaner.resched_event != NULL) {
340		isc_event_free(&cache->cleaner.resched_event);
341	}
342
343	if (cache->cleaner.iterator != NULL) {
344		dns_dbiterator_destroy(&cache->cleaner.iterator);
345	}
346
347	isc_mutex_destroy(&cache->cleaner.lock);
348
349	if (cache->db != NULL) {
350		dns_db_detach(&cache->db);
351	}
352
353	if (cache->db_argv != NULL) {
354		/*
355		 * We don't free db_argv[0] in "rbt" cache databases
356		 * as it's a pointer to hmctx
357		 */
358		int extra = 0;
359		if (strcmp(cache->db_type, "rbt") == 0) {
360			extra = 1;
361		}
362		for (int i = extra; i < cache->db_argc; i++) {
363			if (cache->db_argv[i] != NULL) {
364				isc_mem_free(cache->mctx, cache->db_argv[i]);
365			}
366		}
367		isc_mem_put(cache->mctx, cache->db_argv,
368			    cache->db_argc * sizeof(char *));
369	}
370
371	if (cache->db_type != NULL) {
372		isc_mem_free(cache->mctx, cache->db_type);
373	}
374
375	if (cache->name != NULL) {
376		isc_mem_free(cache->mctx, cache->name);
377	}
378
379	if (cache->stats != NULL) {
380		isc_stats_detach(&cache->stats);
381	}
382
383	isc_mutex_destroy(&cache->lock);
384
385	cache->magic = 0;
386	isc_mem_detach(&cache->hmctx);
387	isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
388}
389
390void
391dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) {
392	REQUIRE(VALID_CACHE(cache));
393	REQUIRE(targetp != NULL && *targetp == NULL);
394
395	isc_refcount_increment(&cache->references);
396
397	*targetp = cache;
398}
399
400void
401dns_cache_detach(dns_cache_t **cachep) {
402	dns_cache_t *cache;
403
404	REQUIRE(cachep != NULL);
405	cache = *cachep;
406	*cachep = NULL;
407	REQUIRE(VALID_CACHE(cache));
408
409	if (isc_refcount_decrement(&cache->references) == 1) {
410		cache->cleaner.overmem = false;
411
412		/*
413		 * If the cleaner task exists, let it free the cache.
414		 */
415		if (isc_refcount_decrement(&cache->live_tasks) > 1) {
416			isc_task_shutdown(cache->cleaner.task);
417		} else {
418			cache_free(cache);
419		}
420	}
421}
422
423void
424dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) {
425	REQUIRE(VALID_CACHE(cache));
426	REQUIRE(dbp != NULL && *dbp == NULL);
427	REQUIRE(cache->db != NULL);
428
429	LOCK(&cache->lock);
430	dns_db_attach(cache->db, dbp);
431	UNLOCK(&cache->lock);
432}
433
434const char *
435dns_cache_getname(dns_cache_t *cache) {
436	REQUIRE(VALID_CACHE(cache));
437
438	return (cache->name);
439}
440
441/*
442 * Initialize the cache cleaner object at *cleaner.
443 * Space for the object must be allocated by the caller.
444 */
445
446static isc_result_t
447cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
448		   isc_timermgr_t *timermgr, cache_cleaner_t *cleaner) {
449	isc_result_t result;
450
451	isc_mutex_init(&cleaner->lock);
452
453	cleaner->increment = DNS_CACHE_CLEANERINCREMENT;
454	cleaner->state = cleaner_s_idle;
455	cleaner->cache = cache;
456	cleaner->iterator = NULL;
457	cleaner->overmem = false;
458	cleaner->replaceiterator = false;
459
460	cleaner->task = NULL;
461	cleaner->resched_event = NULL;
462	cleaner->overmem_event = NULL;
463
464	result = dns_db_createiterator(cleaner->cache->db, false,
465				       &cleaner->iterator);
466	if (result != ISC_R_SUCCESS) {
467		goto cleanup;
468	}
469
470	if (taskmgr != NULL && timermgr != NULL) {
471		result = isc_task_create(taskmgr, 1, &cleaner->task);
472		if (result != ISC_R_SUCCESS) {
473			UNEXPECTED_ERROR("isc_task_create() failed: %s",
474					 isc_result_totext(result));
475			result = ISC_R_UNEXPECTED;
476			goto cleanup;
477		}
478		isc_refcount_increment(&cleaner->cache->live_tasks);
479		isc_task_setname(cleaner->task, "cachecleaner", cleaner);
480
481		result = isc_task_onshutdown(cleaner->task,
482					     cleaner_shutdown_action, cache);
483		if (result != ISC_R_SUCCESS) {
484			isc_refcount_decrement0(&cleaner->cache->live_tasks);
485			UNEXPECTED_ERROR("cache cleaner: "
486					 "isc_task_onshutdown() failed: %s",
487					 isc_result_totext(result));
488			goto cleanup;
489		}
490
491		cleaner->resched_event = isc_event_allocate(
492			cache->mctx, cleaner, DNS_EVENT_CACHECLEAN,
493			incremental_cleaning_action, cleaner,
494			sizeof(isc_event_t));
495
496		cleaner->overmem_event = isc_event_allocate(
497			cache->mctx, cleaner, DNS_EVENT_CACHEOVERMEM,
498			overmem_cleaning_action, cleaner, sizeof(isc_event_t));
499	}
500
501	return (ISC_R_SUCCESS);
502
503cleanup:
504	if (cleaner->overmem_event != NULL) {
505		isc_event_free(&cleaner->overmem_event);
506	}
507	if (cleaner->resched_event != NULL) {
508		isc_event_free(&cleaner->resched_event);
509	}
510	if (cleaner->task != NULL) {
511		isc_task_detach(&cleaner->task);
512	}
513	if (cleaner->iterator != NULL) {
514		dns_dbiterator_destroy(&cleaner->iterator);
515	}
516	isc_mutex_destroy(&cleaner->lock);
517
518	return (result);
519}
520
521static void
522begin_cleaning(cache_cleaner_t *cleaner) {
523	isc_result_t result = ISC_R_SUCCESS;
524
525	REQUIRE(CLEANER_IDLE(cleaner));
526
527	/*
528	 * Create an iterator, if it does not already exist, and
529	 * position it at the beginning of the cache.
530	 */
531	if (cleaner->iterator == NULL) {
532		result = dns_db_createiterator(cleaner->cache->db, false,
533					       &cleaner->iterator);
534	}
535	if (result != ISC_R_SUCCESS) {
536		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
537			      DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
538			      "cache cleaner could not create "
539			      "iterator: %s",
540			      isc_result_totext(result));
541	} else {
542		dns_dbiterator_setcleanmode(cleaner->iterator, true);
543		result = dns_dbiterator_first(cleaner->iterator);
544	}
545	if (result != ISC_R_SUCCESS) {
546		/*
547		 * If the result is ISC_R_NOMORE, the database is empty,
548		 * so there is nothing to be cleaned.
549		 */
550		if (result != ISC_R_NOMORE && cleaner->iterator != NULL) {
551			UNEXPECTED_ERROR("cache cleaner: "
552					 "dns_dbiterator_first() failed: %s",
553					 isc_result_totext(result));
554			dns_dbiterator_destroy(&cleaner->iterator);
555		} else if (cleaner->iterator != NULL) {
556			result = dns_dbiterator_pause(cleaner->iterator);
557			RUNTIME_CHECK(result == ISC_R_SUCCESS);
558		}
559	} else {
560		/*
561		 * Pause the iterator to free its lock.
562		 */
563		result = dns_dbiterator_pause(cleaner->iterator);
564		RUNTIME_CHECK(result == ISC_R_SUCCESS);
565
566		isc_log_write(
567			dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
568			ISC_LOG_DEBUG(1), "begin cache cleaning, mem inuse %lu",
569			(unsigned long)isc_mem_inuse(cleaner->cache->mctx));
570		cleaner->state = cleaner_s_busy;
571		isc_task_send(cleaner->task, &cleaner->resched_event);
572	}
573
574	return;
575}
576
577static void
578end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) {
579	isc_result_t result;
580
581	REQUIRE(CLEANER_BUSY(cleaner));
582	REQUIRE(event != NULL);
583
584	result = dns_dbiterator_pause(cleaner->iterator);
585	if (result != ISC_R_SUCCESS) {
586		dns_dbiterator_destroy(&cleaner->iterator);
587	}
588
589	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
590		      ISC_LOG_DEBUG(1), "end cache cleaning, mem inuse %lu",
591		      (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
592
593	cleaner->state = cleaner_s_idle;
594	cleaner->resched_event = event;
595}
596
597/*
598 * This is called when the cache either surpasses its upper limit
599 * or shrinks beyond its lower limit.
600 */
601static void
602overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
603	cache_cleaner_t *cleaner = event->ev_arg;
604	bool want_cleaning = false;
605
606	UNUSED(task);
607
608	INSIST(task == cleaner->task);
609	INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM);
610	INSIST(cleaner->overmem_event == NULL);
611
612	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
613		      ISC_LOG_DEBUG(1),
614		      "overmem_cleaning_action called, "
615		      "overmem = %d, state = %d",
616		      cleaner->overmem, cleaner->state);
617
618	LOCK(&cleaner->lock);
619
620	if (cleaner->overmem) {
621		if (cleaner->state == cleaner_s_idle) {
622			want_cleaning = true;
623		}
624	} else {
625		if (cleaner->state == cleaner_s_busy) {
626			/*
627			 * end_cleaning() can't be called here because
628			 * then both cleaner->overmem_event and
629			 * cleaner->resched_event will point to this
630			 * event.  Set the state to done, and then
631			 * when the incremental_cleaning_action() event
632			 * is posted, it will handle the end_cleaning.
633			 */
634			cleaner->state = cleaner_s_done;
635		}
636	}
637
638	cleaner->overmem_event = event;
639
640	UNLOCK(&cleaner->lock);
641
642	if (want_cleaning) {
643		begin_cleaning(cleaner);
644	}
645}
646
647/*
648 * Do incremental cleaning.
649 */
650static void
651incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
652	cache_cleaner_t *cleaner = event->ev_arg;
653	isc_result_t result;
654	unsigned int n_names;
655	isc_time_t start;
656
657	UNUSED(task);
658
659	INSIST(task == cleaner->task);
660	INSIST(event->ev_type == DNS_EVENT_CACHECLEAN);
661
662	if (cleaner->state == cleaner_s_done) {
663		cleaner->state = cleaner_s_busy;
664		end_cleaning(cleaner, event);
665		LOCK(&cleaner->cache->lock);
666		LOCK(&cleaner->lock);
667		if (cleaner->replaceiterator) {
668			dns_dbiterator_destroy(&cleaner->iterator);
669			(void)dns_db_createiterator(cleaner->cache->db, false,
670						    &cleaner->iterator);
671			cleaner->replaceiterator = false;
672		}
673		UNLOCK(&cleaner->lock);
674		UNLOCK(&cleaner->cache->lock);
675		return;
676	}
677
678	INSIST(CLEANER_BUSY(cleaner));
679
680	n_names = cleaner->increment;
681
682	REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator));
683
684	isc_time_now(&start);
685	while (n_names-- > 0) {
686		dns_dbnode_t *node = NULL;
687
688		result = dns_dbiterator_current(cleaner->iterator, &node, NULL);
689		if (result != ISC_R_SUCCESS) {
690			UNEXPECTED_ERROR("cache cleaner: "
691					 "dns_dbiterator_current() failed: %s",
692					 isc_result_totext(result));
693
694			end_cleaning(cleaner, event);
695			return;
696		}
697
698		/*
699		 * The node was not needed, but was required by
700		 * dns_dbiterator_current().  Give up its reference.
701		 */
702		dns_db_detachnode(cleaner->cache->db, &node);
703
704		/*
705		 * Step to the next node.
706		 */
707		result = dns_dbiterator_next(cleaner->iterator);
708
709		if (result != ISC_R_SUCCESS) {
710			/*
711			 * Either the end was reached (ISC_R_NOMORE) or
712			 * some error was signaled.  If the cache is still
713			 * overmem and no error was encountered,
714			 * keep trying to clean it, otherwise stop cleaning.
715			 */
716			if (result != ISC_R_NOMORE) {
717				UNEXPECTED_ERROR("cache cleaner: "
718						 "dns_dbiterator_next() "
719						 "failed: %s",
720						 isc_result_totext(result));
721			} else if (cleaner->overmem) {
722				result =
723					dns_dbiterator_first(cleaner->iterator);
724				if (result == ISC_R_SUCCESS) {
725					isc_log_write(dns_lctx,
726						      DNS_LOGCATEGORY_DATABASE,
727						      DNS_LOGMODULE_CACHE,
728						      ISC_LOG_DEBUG(1),
729						      "cache cleaner: "
730						      "still overmem, "
731						      "reset and try again");
732					continue;
733				}
734			}
735
736			end_cleaning(cleaner, event);
737			return;
738		}
739	}
740
741	/*
742	 * We have successfully performed a cleaning increment but have
743	 * not gone through the entire cache.  Free the iterator locks
744	 * and reschedule another batch.  If it fails, just try to continue
745	 * anyway.
746	 */
747	result = dns_dbiterator_pause(cleaner->iterator);
748	RUNTIME_CHECK(result == ISC_R_SUCCESS);
749
750	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
751		      ISC_LOG_DEBUG(1),
752		      "cache cleaner: checked %u nodes, "
753		      "mem inuse %lu, sleeping",
754		      cleaner->increment,
755		      (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
756
757	isc_task_send(task, &event);
758	INSIST(CLEANER_BUSY(cleaner));
759	return;
760}
761
762/*
763 * Do immediate cleaning.
764 */
765isc_result_t
766dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
767	isc_result_t result;
768	dns_dbiterator_t *iterator = NULL;
769
770	REQUIRE(VALID_CACHE(cache));
771
772	result = dns_db_createiterator(cache->db, 0, &iterator);
773	if (result != ISC_R_SUCCESS) {
774		return (result);
775	}
776
777	result = dns_dbiterator_first(iterator);
778
779	while (result == ISC_R_SUCCESS) {
780		dns_dbnode_t *node = NULL;
781		result = dns_dbiterator_current(iterator, &node,
782						(dns_name_t *)NULL);
783		if (result != ISC_R_SUCCESS) {
784			break;
785		}
786
787		/*
788		 * Check TTLs, mark expired rdatasets stale.
789		 */
790		result = dns_db_expirenode(cache->db, node, now);
791		if (result != ISC_R_SUCCESS) {
792			UNEXPECTED_ERROR("cache cleaner: dns_db_expirenode() "
793					 "failed: %s",
794					 isc_result_totext(result));
795			/*
796			 * Continue anyway.
797			 */
798		}
799
800		/*
801		 * This is where the actual freeing takes place.
802		 */
803		dns_db_detachnode(cache->db, &node);
804
805		result = dns_dbiterator_next(iterator);
806	}
807
808	dns_dbiterator_destroy(&iterator);
809
810	if (result == ISC_R_NOMORE) {
811		result = ISC_R_SUCCESS;
812	}
813
814	return (result);
815}
816
817static void
818water(void *arg, int mark) {
819	dns_cache_t *cache = arg;
820	bool overmem = (mark == ISC_MEM_HIWATER);
821
822	REQUIRE(VALID_CACHE(cache));
823
824	LOCK(&cache->cleaner.lock);
825
826	if (overmem != cache->cleaner.overmem) {
827		dns_db_overmem(cache->db, overmem);
828		cache->cleaner.overmem = overmem;
829		isc_mem_waterack(cache->mctx, mark);
830	}
831
832	if (cache->cleaner.overmem_event != NULL) {
833		isc_task_send(cache->cleaner.task,
834			      &cache->cleaner.overmem_event);
835	}
836
837	UNLOCK(&cache->cleaner.lock);
838}
839
840void
841dns_cache_setcachesize(dns_cache_t *cache, size_t size) {
842	size_t hiwater, lowater;
843
844	REQUIRE(VALID_CACHE(cache));
845
846	/*
847	 * Impose a minimum cache size; pathological things happen if there
848	 * is too little room.
849	 */
850	if (size != 0U && size < DNS_CACHE_MINSIZE) {
851		size = DNS_CACHE_MINSIZE;
852	}
853
854	LOCK(&cache->lock);
855	cache->size = size;
856	UNLOCK(&cache->lock);
857
858	hiwater = size - (size >> 3); /* Approximately 7/8ths. */
859	lowater = size - (size >> 2); /* Approximately 3/4ths. */
860
861	/*
862	 * If the cache was overmem and cleaning, but now with the new limits
863	 * it is no longer in an overmem condition, then the next
864	 * isc_mem_put for cache memory will do the right thing and trigger
865	 * water().
866	 */
867
868	if (size == 0U || hiwater == 0U || lowater == 0U) {
869		/*
870		 * Disable cache memory limiting.
871		 */
872		isc_mem_clearwater(cache->mctx);
873	} else {
874		/*
875		 * Establish new cache memory limits (either for the first
876		 * time, or replacing other limits).
877		 */
878		isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
879	}
880}
881
882size_t
883dns_cache_getcachesize(dns_cache_t *cache) {
884	size_t size;
885
886	REQUIRE(VALID_CACHE(cache));
887
888	LOCK(&cache->lock);
889	size = cache->size;
890	UNLOCK(&cache->lock);
891
892	return (size);
893}
894
895void
896dns_cache_setservestalettl(dns_cache_t *cache, dns_ttl_t ttl) {
897	REQUIRE(VALID_CACHE(cache));
898
899	LOCK(&cache->lock);
900	cache->serve_stale_ttl = ttl;
901	UNLOCK(&cache->lock);
902
903	(void)dns_db_setservestalettl(cache->db, ttl);
904}
905
906dns_ttl_t
907dns_cache_getservestalettl(dns_cache_t *cache) {
908	dns_ttl_t ttl;
909	isc_result_t result;
910
911	REQUIRE(VALID_CACHE(cache));
912
913	/*
914	 * Could get it straight from the dns_cache_t, but use db
915	 * to confirm the value that the db is really using.
916	 */
917	result = dns_db_getservestalettl(cache->db, &ttl);
918	return (result == ISC_R_SUCCESS ? ttl : 0);
919}
920
921void
922dns_cache_setservestalerefresh(dns_cache_t *cache, dns_ttl_t interval) {
923	REQUIRE(VALID_CACHE(cache));
924
925	LOCK(&cache->lock);
926	cache->serve_stale_refresh = interval;
927	UNLOCK(&cache->lock);
928
929	(void)dns_db_setservestalerefresh(cache->db, interval);
930}
931
932dns_ttl_t
933dns_cache_getservestalerefresh(dns_cache_t *cache) {
934	isc_result_t result;
935	dns_ttl_t interval;
936
937	REQUIRE(VALID_CACHE(cache));
938
939	result = dns_db_getservestalerefresh(cache->db, &interval);
940	return (result == ISC_R_SUCCESS ? interval : 0);
941}
942
943/*
944 * The cleaner task is shutting down; do the necessary cleanup.
945 */
946static void
947cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
948	dns_cache_t *cache = event->ev_arg;
949
950	UNUSED(task);
951
952	INSIST(task == cache->cleaner.task);
953	INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
954
955	if (CLEANER_BUSY(&cache->cleaner)) {
956		end_cleaning(&cache->cleaner, event);
957	} else {
958		isc_event_free(&event);
959	}
960
961	/* Make sure we don't reschedule anymore. */
962	(void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL);
963
964	isc_refcount_decrementz(&cache->live_tasks);
965
966	cache_free(cache);
967}
968
969isc_result_t
970dns_cache_flush(dns_cache_t *cache) {
971	dns_db_t *db = NULL, *olddb;
972	dns_dbiterator_t *dbiterator = NULL, *olddbiterator = NULL;
973	isc_result_t result;
974
975	result = cache_create_db(cache, &db);
976	if (result != ISC_R_SUCCESS) {
977		return (result);
978	}
979
980	result = dns_db_createiterator(db, false, &dbiterator);
981	if (result != ISC_R_SUCCESS) {
982		dns_db_detach(&db);
983		return (result);
984	}
985
986	LOCK(&cache->lock);
987	LOCK(&cache->cleaner.lock);
988	if (cache->cleaner.state == cleaner_s_idle) {
989		olddbiterator = cache->cleaner.iterator;
990		cache->cleaner.iterator = dbiterator;
991		dbiterator = NULL;
992	} else {
993		if (cache->cleaner.state == cleaner_s_busy) {
994			cache->cleaner.state = cleaner_s_done;
995		}
996		cache->cleaner.replaceiterator = true;
997	}
998	olddb = cache->db;
999	cache->db = db;
1000	dns_db_setcachestats(cache->db, cache->stats);
1001	UNLOCK(&cache->cleaner.lock);
1002	UNLOCK(&cache->lock);
1003
1004	if (dbiterator != NULL) {
1005		dns_dbiterator_destroy(&dbiterator);
1006	}
1007	if (olddbiterator != NULL) {
1008		dns_dbiterator_destroy(&olddbiterator);
1009	}
1010	dns_db_detach(&olddb);
1011
1012	return (ISC_R_SUCCESS);
1013}
1014
1015static isc_result_t
1016clearnode(dns_db_t *db, dns_dbnode_t *node) {
1017	isc_result_t result;
1018	dns_rdatasetiter_t *iter = NULL;
1019
1020	result = dns_db_allrdatasets(db, node, NULL, DNS_DB_STALEOK,
1021				     (isc_stdtime_t)0, &iter);
1022	if (result != ISC_R_SUCCESS) {
1023		return (result);
1024	}
1025
1026	for (result = dns_rdatasetiter_first(iter); result == ISC_R_SUCCESS;
1027	     result = dns_rdatasetiter_next(iter))
1028	{
1029		dns_rdataset_t rdataset;
1030		dns_rdataset_init(&rdataset);
1031
1032		dns_rdatasetiter_current(iter, &rdataset);
1033		result = dns_db_deleterdataset(db, node, NULL, rdataset.type,
1034					       rdataset.covers);
1035		dns_rdataset_disassociate(&rdataset);
1036		if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) {
1037			break;
1038		}
1039	}
1040
1041	if (result == ISC_R_NOMORE) {
1042		result = ISC_R_SUCCESS;
1043	}
1044
1045	dns_rdatasetiter_destroy(&iter);
1046	return (result);
1047}
1048
1049static isc_result_t
1050cleartree(dns_db_t *db, const dns_name_t *name) {
1051	isc_result_t result, answer = ISC_R_SUCCESS;
1052	dns_dbiterator_t *iter = NULL;
1053	dns_dbnode_t *node = NULL, *top = NULL;
1054	dns_fixedname_t fnodename;
1055	dns_name_t *nodename;
1056
1057	/*
1058	 * Create the node if it doesn't exist so dns_dbiterator_seek()
1059	 * can find it.  We will continue even if this fails.
1060	 */
1061	(void)dns_db_findnode(db, name, true, &top);
1062
1063	nodename = dns_fixedname_initname(&fnodename);
1064
1065	result = dns_db_createiterator(db, 0, &iter);
1066	if (result != ISC_R_SUCCESS) {
1067		goto cleanup;
1068	}
1069
1070	result = dns_dbiterator_seek(iter, name);
1071	if (result == DNS_R_PARTIALMATCH) {
1072		result = dns_dbiterator_next(iter);
1073	}
1074	if (result != ISC_R_SUCCESS) {
1075		goto cleanup;
1076	}
1077
1078	while (result == ISC_R_SUCCESS) {
1079		result = dns_dbiterator_current(iter, &node, nodename);
1080		if (result == DNS_R_NEWORIGIN) {
1081			result = ISC_R_SUCCESS;
1082		}
1083		if (result != ISC_R_SUCCESS) {
1084			goto cleanup;
1085		}
1086		/*
1087		 * Are we done?
1088		 */
1089		if (!dns_name_issubdomain(nodename, name)) {
1090			goto cleanup;
1091		}
1092
1093		/*
1094		 * If clearnode fails record and move onto the next node.
1095		 */
1096		result = clearnode(db, node);
1097		if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) {
1098			answer = result;
1099		}
1100		dns_db_detachnode(db, &node);
1101		result = dns_dbiterator_next(iter);
1102	}
1103
1104cleanup:
1105	if (result == ISC_R_NOMORE || result == ISC_R_NOTFOUND) {
1106		result = ISC_R_SUCCESS;
1107	}
1108	if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) {
1109		answer = result;
1110	}
1111	if (node != NULL) {
1112		dns_db_detachnode(db, &node);
1113	}
1114	if (iter != NULL) {
1115		dns_dbiterator_destroy(&iter);
1116	}
1117	if (top != NULL) {
1118		dns_db_detachnode(db, &top);
1119	}
1120
1121	return (answer);
1122}
1123
1124isc_result_t
1125dns_cache_flushname(dns_cache_t *cache, const dns_name_t *name) {
1126	return (dns_cache_flushnode(cache, name, false));
1127}
1128
1129isc_result_t
1130dns_cache_flushnode(dns_cache_t *cache, const dns_name_t *name, bool tree) {
1131	isc_result_t result;
1132	dns_dbnode_t *node = NULL;
1133	dns_db_t *db = NULL;
1134
1135	if (tree && dns_name_equal(name, dns_rootname)) {
1136		return (dns_cache_flush(cache));
1137	}
1138
1139	LOCK(&cache->lock);
1140	if (cache->db != NULL) {
1141		dns_db_attach(cache->db, &db);
1142	}
1143	UNLOCK(&cache->lock);
1144	if (db == NULL) {
1145		return (ISC_R_SUCCESS);
1146	}
1147
1148	if (tree) {
1149		result = cleartree(cache->db, name);
1150	} else {
1151		result = dns_db_findnode(cache->db, name, false, &node);
1152		if (result == ISC_R_NOTFOUND) {
1153			result = ISC_R_SUCCESS;
1154			goto cleanup_db;
1155		}
1156		if (result != ISC_R_SUCCESS) {
1157			goto cleanup_db;
1158		}
1159		result = clearnode(cache->db, node);
1160		dns_db_detachnode(cache->db, &node);
1161	}
1162
1163cleanup_db:
1164	dns_db_detach(&db);
1165	return (result);
1166}
1167
1168isc_stats_t *
1169dns_cache_getstats(dns_cache_t *cache) {
1170	REQUIRE(VALID_CACHE(cache));
1171	return (cache->stats);
1172}
1173
1174void
1175dns_cache_updatestats(dns_cache_t *cache, isc_result_t result) {
1176	REQUIRE(VALID_CACHE(cache));
1177	if (cache->stats == NULL) {
1178		return;
1179	}
1180
1181	switch (result) {
1182	case ISC_R_SUCCESS:
1183	case DNS_R_NCACHENXDOMAIN:
1184	case DNS_R_NCACHENXRRSET:
1185	case DNS_R_CNAME:
1186	case DNS_R_DNAME:
1187	case DNS_R_GLUE:
1188	case DNS_R_ZONECUT:
1189	case DNS_R_COVERINGNSEC:
1190		isc_stats_increment(cache->stats,
1191				    dns_cachestatscounter_queryhits);
1192		break;
1193	default:
1194		isc_stats_increment(cache->stats,
1195				    dns_cachestatscounter_querymisses);
1196	}
1197}
1198
1199/*
1200 * XXX: Much of the following code has been copied in from statschannel.c.
1201 * We should refactor this into a generic function in stats.c that can be
1202 * called from both places.
1203 */
1204typedef struct cache_dumparg {
1205	isc_statsformat_t type;
1206	void *arg;		 /* type dependent argument */
1207	int ncounters;		 /* for general statistics */
1208	int *counterindices;	 /* for general statistics */
1209	uint64_t *countervalues; /* for general statistics */
1210	isc_result_t result;
1211} cache_dumparg_t;
1212
1213static void
1214getcounter(isc_statscounter_t counter, uint64_t val, void *arg) {
1215	cache_dumparg_t *dumparg = arg;
1216
1217	REQUIRE(counter < dumparg->ncounters);
1218	dumparg->countervalues[counter] = val;
1219}
1220
1221static void
1222getcounters(isc_stats_t *stats, isc_statsformat_t type, int ncounters,
1223	    int *indices, uint64_t *values) {
1224	cache_dumparg_t dumparg;
1225
1226	memset(values, 0, sizeof(values[0]) * ncounters);
1227
1228	dumparg.type = type;
1229	dumparg.ncounters = ncounters;
1230	dumparg.counterindices = indices;
1231	dumparg.countervalues = values;
1232
1233	isc_stats_dump(stats, getcounter, &dumparg, ISC_STATSDUMP_VERBOSE);
1234}
1235
1236void
1237dns_cache_dumpstats(dns_cache_t *cache, FILE *fp) {
1238	int indices[dns_cachestatscounter_max];
1239	uint64_t values[dns_cachestatscounter_max];
1240
1241	REQUIRE(VALID_CACHE(cache));
1242
1243	getcounters(cache->stats, isc_statsformat_file,
1244		    dns_cachestatscounter_max, indices, values);
1245
1246	fprintf(fp, "%20" PRIu64 " %s\n", values[dns_cachestatscounter_hits],
1247		"cache hits");
1248	fprintf(fp, "%20" PRIu64 " %s\n", values[dns_cachestatscounter_misses],
1249		"cache misses");
1250	fprintf(fp, "%20" PRIu64 " %s\n",
1251		values[dns_cachestatscounter_queryhits],
1252		"cache hits (from query)");
1253	fprintf(fp, "%20" PRIu64 " %s\n",
1254		values[dns_cachestatscounter_querymisses],
1255		"cache misses (from query)");
1256	fprintf(fp, "%20" PRIu64 " %s\n",
1257		values[dns_cachestatscounter_deletelru],
1258		"cache records deleted due to memory exhaustion");
1259	fprintf(fp, "%20" PRIu64 " %s\n",
1260		values[dns_cachestatscounter_deletettl],
1261		"cache records deleted due to TTL expiration");
1262	fprintf(fp, "%20" PRIu64 " %s\n",
1263		values[dns_cachestatscounter_coveringnsec],
1264		"covering nsec returned");
1265	fprintf(fp, "%20u %s\n", dns_db_nodecount(cache->db, dns_dbtree_main),
1266		"cache database nodes");
1267	fprintf(fp, "%20u %s\n", dns_db_nodecount(cache->db, dns_dbtree_nsec),
1268		"cache NSEC auxiliary database nodes");
1269	fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)dns_db_hashsize(cache->db),
1270		"cache database hash buckets");
1271
1272	fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_total(cache->mctx),
1273		"cache tree memory total");
1274	fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_inuse(cache->mctx),
1275		"cache tree memory in use");
1276	fprintf(fp, "%20" PRIu64 " %s\n",
1277		(uint64_t)isc_mem_maxinuse(cache->mctx),
1278		"cache tree highest memory in use");
1279
1280	fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_total(cache->hmctx),
1281		"cache heap memory total");
1282	fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_inuse(cache->hmctx),
1283		"cache heap memory in use");
1284	fprintf(fp, "%20" PRIu64 " %s\n",
1285		(uint64_t)isc_mem_maxinuse(cache->hmctx),
1286		"cache heap highest memory in use");
1287}
1288
1289#ifdef HAVE_LIBXML2
1290#define TRY0(a)                     \
1291	do {                        \
1292		xmlrc = (a);        \
1293		if (xmlrc < 0)      \
1294			goto error; \
1295	} while (0)
1296static int
1297renderstat(const char *name, uint64_t value, xmlTextWriterPtr writer) {
1298	int xmlrc;
1299
1300	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
1301	TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
1302					 ISC_XMLCHAR name));
1303	TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", value));
1304	TRY0(xmlTextWriterEndElement(writer)); /* counter */
1305
1306error:
1307	return (xmlrc);
1308}
1309
1310int
1311dns_cache_renderxml(dns_cache_t *cache, void *writer0) {
1312	int indices[dns_cachestatscounter_max];
1313	uint64_t values[dns_cachestatscounter_max];
1314	int xmlrc;
1315	xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0;
1316
1317	REQUIRE(VALID_CACHE(cache));
1318
1319	getcounters(cache->stats, isc_statsformat_file,
1320		    dns_cachestatscounter_max, indices, values);
1321	TRY0(renderstat("CacheHits", values[dns_cachestatscounter_hits],
1322			writer));
1323	TRY0(renderstat("CacheMisses", values[dns_cachestatscounter_misses],
1324			writer));
1325	TRY0(renderstat("QueryHits", values[dns_cachestatscounter_queryhits],
1326			writer));
1327	TRY0(renderstat("QueryMisses",
1328			values[dns_cachestatscounter_querymisses], writer));
1329	TRY0(renderstat("DeleteLRU", values[dns_cachestatscounter_deletelru],
1330			writer));
1331	TRY0(renderstat("DeleteTTL", values[dns_cachestatscounter_deletettl],
1332			writer));
1333	TRY0(renderstat("CoveringNSEC",
1334			values[dns_cachestatscounter_coveringnsec], writer));
1335
1336	TRY0(renderstat("CacheNodes",
1337			dns_db_nodecount(cache->db, dns_dbtree_main), writer));
1338	TRY0(renderstat("CacheNSECNodes",
1339			dns_db_nodecount(cache->db, dns_dbtree_nsec), writer));
1340	TRY0(renderstat("CacheBuckets", dns_db_hashsize(cache->db), writer));
1341
1342	TRY0(renderstat("TreeMemTotal", isc_mem_total(cache->mctx), writer));
1343	TRY0(renderstat("TreeMemInUse", isc_mem_inuse(cache->mctx), writer));
1344	TRY0(renderstat("TreeMemMax", isc_mem_maxinuse(cache->mctx), writer));
1345
1346	TRY0(renderstat("HeapMemTotal", isc_mem_total(cache->hmctx), writer));
1347	TRY0(renderstat("HeapMemInUse", isc_mem_inuse(cache->hmctx), writer));
1348	TRY0(renderstat("HeapMemMax", isc_mem_maxinuse(cache->hmctx), writer));
1349error:
1350	return (xmlrc);
1351}
1352#endif /* ifdef HAVE_LIBXML2 */
1353
1354#ifdef HAVE_JSON_C
1355#define CHECKMEM(m)                              \
1356	do {                                     \
1357		if (m == NULL) {                 \
1358			result = ISC_R_NOMEMORY; \
1359			goto error;              \
1360		}                                \
1361	} while (0)
1362
1363isc_result_t
1364dns_cache_renderjson(dns_cache_t *cache, void *cstats0) {
1365	isc_result_t result = ISC_R_SUCCESS;
1366	int indices[dns_cachestatscounter_max];
1367	uint64_t values[dns_cachestatscounter_max];
1368	json_object *obj;
1369	json_object *cstats = (json_object *)cstats0;
1370
1371	REQUIRE(VALID_CACHE(cache));
1372
1373	getcounters(cache->stats, isc_statsformat_file,
1374		    dns_cachestatscounter_max, indices, values);
1375
1376	obj = json_object_new_int64(values[dns_cachestatscounter_hits]);
1377	CHECKMEM(obj);
1378	json_object_object_add(cstats, "CacheHits", obj);
1379
1380	obj = json_object_new_int64(values[dns_cachestatscounter_misses]);
1381	CHECKMEM(obj);
1382	json_object_object_add(cstats, "CacheMisses", obj);
1383
1384	obj = json_object_new_int64(values[dns_cachestatscounter_queryhits]);
1385	CHECKMEM(obj);
1386	json_object_object_add(cstats, "QueryHits", obj);
1387
1388	obj = json_object_new_int64(values[dns_cachestatscounter_querymisses]);
1389	CHECKMEM(obj);
1390	json_object_object_add(cstats, "QueryMisses", obj);
1391
1392	obj = json_object_new_int64(values[dns_cachestatscounter_deletelru]);
1393	CHECKMEM(obj);
1394	json_object_object_add(cstats, "DeleteLRU", obj);
1395
1396	obj = json_object_new_int64(values[dns_cachestatscounter_deletettl]);
1397	CHECKMEM(obj);
1398	json_object_object_add(cstats, "DeleteTTL", obj);
1399
1400	obj = json_object_new_int64(values[dns_cachestatscounter_coveringnsec]);
1401	CHECKMEM(obj);
1402	json_object_object_add(cstats, "CoveringNSEC", obj);
1403
1404	obj = json_object_new_int64(
1405		dns_db_nodecount(cache->db, dns_dbtree_main));
1406	CHECKMEM(obj);
1407	json_object_object_add(cstats, "CacheNodes", obj);
1408
1409	obj = json_object_new_int64(
1410		dns_db_nodecount(cache->db, dns_dbtree_nsec));
1411	CHECKMEM(obj);
1412	json_object_object_add(cstats, "CacheNSECNodes", obj);
1413
1414	obj = json_object_new_int64(dns_db_hashsize(cache->db));
1415	CHECKMEM(obj);
1416	json_object_object_add(cstats, "CacheBuckets", obj);
1417
1418	obj = json_object_new_int64(isc_mem_total(cache->mctx));
1419	CHECKMEM(obj);
1420	json_object_object_add(cstats, "TreeMemTotal", obj);
1421
1422	obj = json_object_new_int64(isc_mem_inuse(cache->mctx));
1423	CHECKMEM(obj);
1424	json_object_object_add(cstats, "TreeMemInUse", obj);
1425
1426	obj = json_object_new_int64(isc_mem_maxinuse(cache->mctx));
1427	CHECKMEM(obj);
1428	json_object_object_add(cstats, "TreeMemMax", obj);
1429
1430	obj = json_object_new_int64(isc_mem_total(cache->hmctx));
1431	CHECKMEM(obj);
1432	json_object_object_add(cstats, "HeapMemTotal", obj);
1433
1434	obj = json_object_new_int64(isc_mem_inuse(cache->hmctx));
1435	CHECKMEM(obj);
1436	json_object_object_add(cstats, "HeapMemInUse", obj);
1437
1438	obj = json_object_new_int64(isc_mem_maxinuse(cache->hmctx));
1439	CHECKMEM(obj);
1440	json_object_object_add(cstats, "HeapMemMax", obj);
1441
1442	result = ISC_R_SUCCESS;
1443error:
1444	return (result);
1445}
1446#endif /* ifdef HAVE_JSON_C */
1447