/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 */ |