Coverage Report

Created: 2026-07-30 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/cache.c
Line
Count
Source
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
/*! \file */
15
16
#include <inttypes.h>
17
#include <stdbool.h>
18
19
#include <isc/log.h>
20
#include <isc/loop.h>
21
#include <isc/mem.h>
22
#include <isc/refcount.h>
23
#include <isc/result.h>
24
#include <isc/stats.h>
25
#include <isc/string.h>
26
#include <isc/time.h>
27
#include <isc/timer.h>
28
#include <isc/util.h>
29
30
#include <dns/cache.h>
31
#include <dns/db.h>
32
#include <dns/dbiterator.h>
33
#include <dns/masterdump.h>
34
#include <dns/rdata.h>
35
#include <dns/rdataset.h>
36
#include <dns/rdatasetiter.h>
37
#include <dns/stats.h>
38
39
#ifdef HAVE_JSON_C
40
#include <json_object.h>
41
#endif /* HAVE_JSON_C */
42
43
#ifdef HAVE_LIBXML2
44
#include <libxml/xmlwriter.h>
45
#define ISC_XMLCHAR (const xmlChar *)
46
#endif /* HAVE_LIBXML2 */
47
48
0
#define CACHE_MAGIC    ISC_MAGIC('$', '$', '$', '$')
49
#define VALID_CACHE(cache) ISC_MAGIC_VALID(cache, CACHE_MAGIC)
50
51
/***
52
 ***  Types
53
 ***/
54
55
/*%
56
 * The actual cache object.
57
 */
58
59
struct dns_cache {
60
  /* Unlocked. */
61
  unsigned int magic;
62
  isc_mutex_t lock;
63
  isc_mem_t *mctx;  /* Memory context for the dns_cache object */
64
  isc_mem_t *tmctx; /* Tree memory */
65
  char *name;
66
  isc_refcount_t references;
67
68
  /* Locked by 'lock'. */
69
  dns_rdataclass_t rdclass;
70
  dns_db_t *db;
71
  size_t size;
72
  dns_ttl_t serve_stale_ttl;
73
  dns_ttl_t serve_stale_refresh;
74
  isc_stats_t *stats;
75
  uint32_t maxrrperset;
76
  uint32_t maxtypepername;
77
};
78
79
/***
80
 ***  Functions
81
 ***/
82
83
static isc_result_t
84
0
cache_create_db(dns_cache_t *cache, dns_db_t **dbp, isc_mem_t **tmctxp) {
85
0
  isc_result_t result;
86
0
  dns_db_t *db = NULL;
87
0
  isc_mem_t *tmctx = NULL;
88
89
  /*
90
   * This will be the cache memory context, which is subject
91
   * to cleaning when the configured memory limits are exceeded.
92
   */
93
0
  isc_mem_create("cache", &tmctx);
94
95
0
  result = dns_db_create(tmctx, CACHEDB_DEFAULT, dns_rootname,
96
0
             dns_dbtype_cache, cache->rdclass, 0, NULL, &db);
97
0
  if (result != ISC_R_SUCCESS) {
98
0
    goto cleanup_mctx;
99
0
  }
100
0
  result = dns_db_setcachestats(db, cache->stats);
101
0
  if (result != ISC_R_SUCCESS) {
102
0
    goto cleanup_db;
103
0
  }
104
105
0
  dns_db_setservestalettl(db, cache->serve_stale_ttl);
106
0
  dns_db_setservestalerefresh(db, cache->serve_stale_refresh);
107
0
  dns_db_setmaxrrperset(db, cache->maxrrperset);
108
0
  dns_db_setmaxtypepername(db, cache->maxtypepername);
109
110
0
  *dbp = db;
111
0
  *tmctxp = tmctx;
112
113
0
  return ISC_R_SUCCESS;
114
115
0
cleanup_db:
116
0
  dns_db_detach(&db);
117
0
cleanup_mctx:
118
0
  isc_mem_detach(&tmctx);
119
120
0
  return result;
121
0
}
122
123
static void
124
0
cache_destroy(dns_cache_t *cache) {
125
0
  isc_stats_detach(&cache->stats);
126
0
  isc_mutex_destroy(&cache->lock);
127
0
  isc_mem_free(cache->mctx, cache->name);
128
0
  if (cache->tmctx != NULL) {
129
0
    isc_mem_detach(&cache->tmctx);
130
0
  }
131
0
  isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
132
0
}
133
134
isc_result_t
135
dns_cache_create(dns_rdataclass_t rdclass, const char *cachename,
136
0
     isc_mem_t *mctx, dns_cache_t **cachep) {
137
0
  isc_result_t result;
138
0
  dns_cache_t *cache = NULL;
139
140
0
  REQUIRE(cachename != NULL);
141
0
  REQUIRE(cachep != NULL && *cachep == NULL);
142
143
0
  cache = isc_mem_get(mctx, sizeof(*cache));
144
0
  *cache = (dns_cache_t){
145
0
    .rdclass = rdclass,
146
0
    .name = isc_mem_strdup(mctx, cachename),
147
0
    .references = ISC_REFCOUNT_INITIALIZER(1),
148
0
    .magic = CACHE_MAGIC,
149
0
  };
150
151
0
  isc_mutex_init(&cache->lock);
152
0
  isc_mem_attach(mctx, &cache->mctx);
153
154
0
  isc_stats_create(mctx, &cache->stats, dns_cachestatscounter_max);
155
156
  /*
157
   * Create the database
158
   */
159
0
  CHECK(cache_create_db(cache, &cache->db, &cache->tmctx));
160
161
0
  *cachep = cache;
162
0
  return ISC_R_SUCCESS;
163
164
0
cleanup:
165
0
  cache_destroy(cache);
166
0
  return result;
167
0
}
168
169
static void
170
0
cache_cleanup(dns_cache_t *cache) {
171
0
  REQUIRE(VALID_CACHE(cache));
172
173
0
  isc_refcount_destroy(&cache->references);
174
0
  cache->magic = 0;
175
176
0
  isc_mem_clearwater(cache->tmctx);
177
0
  dns_db_detach(&cache->db);
178
179
0
  cache_destroy(cache);
180
0
}
181
182
#if DNS_CACHE_TRACE
183
ISC_REFCOUNT_TRACE_IMPL(dns_cache, cache_cleanup);
184
#else
185
0
ISC_REFCOUNT_IMPL(dns_cache, cache_cleanup);
Unexecuted instantiation: dns_cache_ref
Unexecuted instantiation: dns_cache_unref
Unexecuted instantiation: dns_cache_detach
186
0
#endif
187
0
188
0
void
189
0
dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) {
190
0
  REQUIRE(VALID_CACHE(cache));
191
0
  REQUIRE(dbp != NULL && *dbp == NULL);
192
0
  REQUIRE(cache->db != NULL);
193
194
0
  LOCK(&cache->lock);
195
0
  dns_db_attach(cache->db, dbp);
196
0
  UNLOCK(&cache->lock);
197
0
}
198
199
const char *
200
0
dns_cache_getname(dns_cache_t *cache) {
201
0
  REQUIRE(VALID_CACHE(cache));
202
203
0
  return cache->name;
204
0
}
205
206
static void
207
0
updatewater(dns_cache_t *cache) {
208
0
  size_t hi = cache->size - (cache->size >> 3); /* ~ 7/8ths. */
209
0
  size_t lo = cache->size - (cache->size >> 2); /* ~ 3/4ths. */
210
0
  isc_mem_setwater(cache->tmctx, hi, lo);
211
0
}
212
213
void
214
0
dns_cache_setcachesize(dns_cache_t *cache, size_t size) {
215
0
  REQUIRE(VALID_CACHE(cache));
216
217
  /*
218
   * Impose a minimum cache size; pathological things happen if there
219
   * is too little room.
220
   */
221
0
  if (size < DNS_CACHE_MINSIZE) {
222
0
    size = DNS_CACHE_MINSIZE;
223
0
  }
224
225
0
  LOCK(&cache->lock);
226
0
  cache->size = size;
227
0
  updatewater(cache);
228
0
  UNLOCK(&cache->lock);
229
0
}
230
231
size_t
232
0
dns_cache_getcachesize(dns_cache_t *cache) {
233
0
  size_t size;
234
235
0
  REQUIRE(VALID_CACHE(cache));
236
237
0
  LOCK(&cache->lock);
238
0
  size = cache->size;
239
0
  UNLOCK(&cache->lock);
240
241
0
  return size;
242
0
}
243
244
void
245
0
dns_cache_setservestalettl(dns_cache_t *cache, dns_ttl_t ttl) {
246
0
  REQUIRE(VALID_CACHE(cache));
247
248
0
  LOCK(&cache->lock);
249
0
  cache->serve_stale_ttl = ttl;
250
0
  UNLOCK(&cache->lock);
251
252
0
  (void)dns_db_setservestalettl(cache->db, ttl);
253
0
}
254
255
dns_ttl_t
256
0
dns_cache_getservestalettl(dns_cache_t *cache) {
257
0
  dns_ttl_t ttl;
258
0
  isc_result_t result;
259
260
0
  REQUIRE(VALID_CACHE(cache));
261
262
  /*
263
   * Could get it straight from the dns_cache_t, but use db
264
   * to confirm the value that the db is really using.
265
   */
266
0
  result = dns_db_getservestalettl(cache->db, &ttl);
267
0
  return result == ISC_R_SUCCESS ? ttl : 0;
268
0
}
269
270
void
271
0
dns_cache_setservestalerefresh(dns_cache_t *cache, dns_ttl_t interval) {
272
0
  REQUIRE(VALID_CACHE(cache));
273
274
0
  LOCK(&cache->lock);
275
0
  cache->serve_stale_refresh = interval;
276
0
  UNLOCK(&cache->lock);
277
278
0
  (void)dns_db_setservestalerefresh(cache->db, interval);
279
0
}
280
281
dns_ttl_t
282
0
dns_cache_getservestalerefresh(dns_cache_t *cache) {
283
0
  isc_result_t result;
284
0
  dns_ttl_t interval;
285
286
0
  REQUIRE(VALID_CACHE(cache));
287
288
0
  result = dns_db_getservestalerefresh(cache->db, &interval);
289
0
  return result == ISC_R_SUCCESS ? interval : 0;
290
0
}
291
292
isc_result_t
293
0
dns_cache_flush(dns_cache_t *cache) {
294
0
  dns_db_t *db = NULL, *olddb = NULL;
295
0
  isc_mem_t *tmctx = NULL, *oldtmctx = NULL;
296
297
0
  RETERR(cache_create_db(cache, &db, &tmctx));
298
299
0
  LOCK(&cache->lock);
300
0
  isc_mem_clearwater(cache->tmctx);
301
0
  oldtmctx = cache->tmctx;
302
0
  cache->tmctx = tmctx;
303
0
  updatewater(cache);
304
0
  olddb = cache->db;
305
0
  cache->db = db;
306
0
  UNLOCK(&cache->lock);
307
308
0
  dns_db_detach(&olddb);
309
0
  isc_mem_detach(&oldtmctx);
310
311
0
  return ISC_R_SUCCESS;
312
0
}
313
314
static isc_result_t
315
0
clearnode(dns_db_t *db, dns_dbnode_t *node) {
316
0
  dns_rdatasetiter_t *iter = NULL;
317
318
0
  RETERR(dns_db_allrdatasets(db, node, NULL, DNS_DB_STALEOK,
319
0
           (isc_stdtime_t)0, &iter));
320
321
0
  DNS_RDATASETITER_FOREACH(iter) {
322
0
    isc_result_t result;
323
0
    dns_rdataset_t rdataset = DNS_RDATASET_INIT;
324
325
0
    dns_rdatasetiter_current(iter, &rdataset);
326
0
    result = dns_db_deleterdataset(db, node, NULL, rdataset.type,
327
0
                 rdataset.covers);
328
0
    dns_rdataset_disassociate(&rdataset);
329
0
    if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) {
330
0
      break;
331
0
    }
332
0
  }
333
334
0
  dns_rdatasetiter_destroy(&iter);
335
0
  return ISC_R_SUCCESS;
336
0
}
337
338
static isc_result_t
339
0
cleartree(dns_db_t *db, const dns_name_t *name) {
340
0
  isc_result_t result, answer = ISC_R_SUCCESS;
341
0
  dns_dbiterator_t *iter = NULL;
342
0
  dns_dbnode_t *node = NULL, *top = NULL;
343
0
  dns_fixedname_t fnodename;
344
0
  dns_name_t *nodename;
345
346
  /*
347
   * Create the node if it doesn't exist so dns_dbiterator_seek()
348
   * can find it.  We will continue even if this fails.
349
   */
350
0
  (void)dns_db_findnode(db, name, true, &top);
351
352
0
  nodename = dns_fixedname_initname(&fnodename);
353
354
0
  CHECK(dns_db_createiterator(db, 0, &iter));
355
356
0
  result = dns_dbiterator_seek(iter, name);
357
0
  if (result == DNS_R_PARTIALMATCH) {
358
0
    result = dns_dbiterator_next(iter);
359
0
  }
360
0
  if (result != ISC_R_SUCCESS) {
361
0
    goto cleanup;
362
0
  }
363
364
0
  while (result == ISC_R_SUCCESS) {
365
0
    result = dns_dbiterator_current(iter, &node, nodename);
366
0
    if (result == DNS_R_NEWORIGIN) {
367
0
      result = ISC_R_SUCCESS;
368
0
    }
369
0
    if (result != ISC_R_SUCCESS) {
370
0
      goto cleanup;
371
0
    }
372
    /*
373
     * Are we done?
374
     */
375
0
    if (!dns_name_issubdomain(nodename, name)) {
376
0
      goto cleanup;
377
0
    }
378
379
    /*
380
     * If clearnode fails record and move onto the next node.
381
     */
382
0
    result = clearnode(db, node);
383
0
    if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) {
384
0
      answer = result;
385
0
    }
386
0
    dns_db_detachnode(&node);
387
0
    result = dns_dbiterator_next(iter);
388
0
  }
389
390
0
cleanup:
391
0
  if (result == ISC_R_NOMORE || result == ISC_R_NOTFOUND) {
392
0
    result = ISC_R_SUCCESS;
393
0
  }
394
0
  if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) {
395
0
    answer = result;
396
0
  }
397
0
  if (node != NULL) {
398
0
    dns_db_detachnode(&node);
399
0
  }
400
0
  if (iter != NULL) {
401
0
    dns_dbiterator_destroy(&iter);
402
0
  }
403
0
  if (top != NULL) {
404
0
    dns_db_detachnode(&top);
405
0
  }
406
407
0
  return answer;
408
0
}
409
410
isc_result_t
411
0
dns_cache_flushname(dns_cache_t *cache, const dns_name_t *name) {
412
0
  return dns_cache_flushnode(cache, name, false);
413
0
}
414
415
isc_result_t
416
0
dns_cache_flushnode(dns_cache_t *cache, const dns_name_t *name, bool tree) {
417
0
  isc_result_t result;
418
0
  dns_dbnode_t *node = NULL;
419
0
  dns_db_t *db = NULL;
420
421
0
  if (tree && dns_name_equal(name, dns_rootname)) {
422
0
    return dns_cache_flush(cache);
423
0
  }
424
425
0
  LOCK(&cache->lock);
426
0
  if (cache->db != NULL) {
427
0
    dns_db_attach(cache->db, &db);
428
0
  }
429
0
  UNLOCK(&cache->lock);
430
0
  if (db == NULL) {
431
0
    return ISC_R_SUCCESS;
432
0
  }
433
434
0
  if (tree) {
435
0
    result = cleartree(cache->db, name);
436
0
  } else {
437
0
    result = dns_db_findnode(cache->db, name, false, &node);
438
0
    if (result == ISC_R_NOTFOUND) {
439
0
      result = ISC_R_SUCCESS;
440
0
      goto cleanup_db;
441
0
    }
442
0
    if (result != ISC_R_SUCCESS) {
443
0
      goto cleanup_db;
444
0
    }
445
0
    result = clearnode(cache->db, node);
446
0
    dns_db_detachnode(&node);
447
0
  }
448
449
0
cleanup_db:
450
0
  dns_db_detach(&db);
451
0
  return result;
452
0
}
453
454
isc_stats_t *
455
0
dns_cache_getstats(dns_cache_t *cache) {
456
0
  REQUIRE(VALID_CACHE(cache));
457
0
  return cache->stats;
458
0
}
459
460
void
461
0
dns_cache_updatestats(dns_cache_t *cache, isc_result_t result) {
462
0
  REQUIRE(VALID_CACHE(cache));
463
0
  if (cache->stats == NULL) {
464
0
    return;
465
0
  }
466
467
0
  switch (result) {
468
0
  case ISC_R_SUCCESS:
469
0
  case DNS_R_NCACHENXDOMAIN:
470
0
  case DNS_R_NCACHENXRRSET:
471
0
  case DNS_R_CNAME:
472
0
  case DNS_R_DNAME:
473
0
  case DNS_R_GLUE:
474
0
  case DNS_R_ZONECUT:
475
0
  case DNS_R_COVERINGNSEC:
476
0
    isc_stats_increment(cache->stats,
477
0
            dns_cachestatscounter_queryhits);
478
0
    break;
479
0
  default:
480
0
    isc_stats_increment(cache->stats,
481
0
            dns_cachestatscounter_querymisses);
482
0
  }
483
0
}
484
485
void
486
0
dns_cache_setmaxrrperset(dns_cache_t *cache, uint32_t value) {
487
0
  REQUIRE(VALID_CACHE(cache));
488
489
0
  cache->maxrrperset = value;
490
0
  if (cache->db != NULL) {
491
0
    dns_db_setmaxrrperset(cache->db, value);
492
0
  }
493
0
}
494
495
void
496
0
dns_cache_setmaxtypepername(dns_cache_t *cache, uint32_t value) {
497
0
  REQUIRE(VALID_CACHE(cache));
498
499
0
  cache->maxtypepername = value;
500
0
  if (cache->db != NULL) {
501
0
    dns_db_setmaxtypepername(cache->db, value);
502
0
  }
503
0
}
504
505
/*
506
 * XXX: Much of the following code has been copied in from statschannel.c.
507
 * We should refactor this into a generic function in stats.c that can be
508
 * called from both places.
509
 */
510
typedef struct cache_dumparg {
511
  isc_statsformat_t type;
512
  void *arg;     /* type dependent argument */
513
  int ncounters;     /* for general statistics */
514
  int *counterindices;   /* for general statistics */
515
  uint64_t *countervalues; /* for general statistics */
516
  isc_result_t result;
517
} cache_dumparg_t;
518
519
static void
520
0
getcounter(isc_statscounter_t counter, uint64_t val, void *arg) {
521
0
  cache_dumparg_t *dumparg = arg;
522
523
0
  REQUIRE(counter < dumparg->ncounters);
524
0
  dumparg->countervalues[counter] = val;
525
0
}
526
527
static void
528
getcounters(isc_stats_t *stats, isc_statsformat_t type, int ncounters,
529
0
      int *indices, uint64_t *values) {
530
0
  cache_dumparg_t dumparg;
531
532
0
  memset(values, 0, sizeof(values[0]) * ncounters);
533
534
0
  dumparg.type = type;
535
0
  dumparg.ncounters = ncounters;
536
0
  dumparg.counterindices = indices;
537
0
  dumparg.countervalues = values;
538
539
0
  isc_stats_dump(stats, getcounter, &dumparg, ISC_STATSDUMP_VERBOSE);
540
0
}
541
542
void
543
0
dns_cache_dumpstats(dns_cache_t *cache, FILE *fp) {
544
0
  int indices[dns_cachestatscounter_max];
545
0
  uint64_t values[dns_cachestatscounter_max];
546
547
0
  REQUIRE(VALID_CACHE(cache));
548
549
0
  getcounters(cache->stats, isc_statsformat_file,
550
0
        dns_cachestatscounter_max, indices, values);
551
552
0
  fprintf(fp, "%20" PRIu64 " %s\n", values[dns_cachestatscounter_hits],
553
0
    "cache hits");
554
0
  fprintf(fp, "%20" PRIu64 " %s\n", values[dns_cachestatscounter_misses],
555
0
    "cache misses");
556
0
  fprintf(fp, "%20" PRIu64 " %s\n",
557
0
    values[dns_cachestatscounter_queryhits],
558
0
    "cache hits (from query)");
559
0
  fprintf(fp, "%20" PRIu64 " %s\n",
560
0
    values[dns_cachestatscounter_querymisses],
561
0
    "cache misses (from query)");
562
0
  fprintf(fp, "%20" PRIu64 " %s\n",
563
0
    values[dns_cachestatscounter_deletelru],
564
0
    "cache records deleted due to memory exhaustion");
565
0
  fprintf(fp, "%20" PRIu64 " %s\n",
566
0
    values[dns_cachestatscounter_coveringnsec],
567
0
    "covering nsec returned");
568
0
  fprintf(fp, "%20u %s\n", dns_db_nodecount(cache->db),
569
0
    "cache database nodes");
570
571
0
  fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_inuse(cache->tmctx),
572
0
    "cache tree memory in use");
573
0
}
574
575
#ifdef HAVE_LIBXML2
576
#define TRY0(a)                     \
577
  do {                        \
578
    xmlrc = (a);        \
579
    if (xmlrc < 0)      \
580
      goto error; \
581
  } while (0)
582
static int
583
renderstat(const char *name, uint64_t value, xmlTextWriterPtr writer) {
584
  int xmlrc;
585
586
  TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
587
  TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
588
           ISC_XMLCHAR name));
589
  TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", value));
590
  TRY0(xmlTextWriterEndElement(writer)); /* counter */
591
592
error:
593
  return xmlrc;
594
}
595
596
int
597
dns_cache_renderxml(dns_cache_t *cache, void *writer0) {
598
  int indices[dns_cachestatscounter_max];
599
  uint64_t values[dns_cachestatscounter_max];
600
  int xmlrc;
601
  xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0;
602
603
  REQUIRE(VALID_CACHE(cache));
604
605
  getcounters(cache->stats, isc_statsformat_file,
606
        dns_cachestatscounter_max, indices, values);
607
  TRY0(renderstat("CacheHits", values[dns_cachestatscounter_hits],
608
      writer));
609
  TRY0(renderstat("CacheMisses", values[dns_cachestatscounter_misses],
610
      writer));
611
  TRY0(renderstat("QueryHits", values[dns_cachestatscounter_queryhits],
612
      writer));
613
  TRY0(renderstat("QueryMisses",
614
      values[dns_cachestatscounter_querymisses], writer));
615
  TRY0(renderstat("DeleteLRU", values[dns_cachestatscounter_deletelru],
616
      writer));
617
  TRY0(renderstat("CoveringNSEC",
618
      values[dns_cachestatscounter_coveringnsec], writer));
619
620
  TRY0(renderstat("CacheNodes", dns_db_nodecount(cache->db), writer));
621
622
  TRY0(renderstat("TreeMemInUse", isc_mem_inuse(cache->tmctx), writer));
623
error:
624
  return xmlrc;
625
}
626
#endif /* ifdef HAVE_LIBXML2 */
627
628
#ifdef HAVE_JSON_C
629
#define CHECKMEM(m)                              \
630
  do {                                     \
631
    if (m == NULL) {                 \
632
      result = ISC_R_NOMEMORY; \
633
      goto error;              \
634
    }                                \
635
  } while (0)
636
637
isc_result_t
638
dns_cache_renderjson(dns_cache_t *cache, void *cstats0) {
639
  isc_result_t result = ISC_R_SUCCESS;
640
  int indices[dns_cachestatscounter_max];
641
  uint64_t values[dns_cachestatscounter_max];
642
  json_object *obj;
643
  json_object *cstats = (json_object *)cstats0;
644
645
  REQUIRE(VALID_CACHE(cache));
646
647
  getcounters(cache->stats, isc_statsformat_file,
648
        dns_cachestatscounter_max, indices, values);
649
650
  obj = json_object_new_int64(values[dns_cachestatscounter_hits]);
651
  CHECKMEM(obj);
652
  json_object_object_add(cstats, "CacheHits", obj);
653
654
  obj = json_object_new_int64(values[dns_cachestatscounter_misses]);
655
  CHECKMEM(obj);
656
  json_object_object_add(cstats, "CacheMisses", obj);
657
658
  obj = json_object_new_int64(values[dns_cachestatscounter_queryhits]);
659
  CHECKMEM(obj);
660
  json_object_object_add(cstats, "QueryHits", obj);
661
662
  obj = json_object_new_int64(values[dns_cachestatscounter_querymisses]);
663
  CHECKMEM(obj);
664
  json_object_object_add(cstats, "QueryMisses", obj);
665
666
  obj = json_object_new_int64(values[dns_cachestatscounter_deletelru]);
667
  CHECKMEM(obj);
668
  json_object_object_add(cstats, "DeleteLRU", obj);
669
670
  obj = json_object_new_int64(values[dns_cachestatscounter_coveringnsec]);
671
  CHECKMEM(obj);
672
  json_object_object_add(cstats, "CoveringNSEC", obj);
673
674
  obj = json_object_new_int64(dns_db_nodecount(cache->db));
675
  CHECKMEM(obj);
676
  json_object_object_add(cstats, "CacheNodes", obj);
677
678
  obj = json_object_new_int64(isc_mem_inuse(cache->tmctx));
679
  CHECKMEM(obj);
680
  json_object_object_add(cstats, "TreeMemInUse", obj);
681
682
  result = ISC_R_SUCCESS;
683
error:
684
  return result;
685
}
686
#endif /* ifdef HAVE_JSON_C */