/src/bind9/lib/dns/cache.c
Line | Count | Source (jump to first uncovered line) |
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 | | * DNS_CACHE_MINSIZE is how many bytes is the floor for |
53 | | * dns_cache_setcachesize(). |
54 | | */ |
55 | 0 | #define DNS_CACHE_MINSIZE 2097152U /*%< Bytes. 2097152 = 2 MB */ |
56 | | |
57 | | /*** |
58 | | *** Types |
59 | | ***/ |
60 | | |
61 | | /*% |
62 | | * The actual cache object. |
63 | | */ |
64 | | |
65 | | struct dns_cache { |
66 | | /* Unlocked. */ |
67 | | unsigned int magic; |
68 | | isc_mutex_t lock; |
69 | | isc_mem_t *mctx; /* Memory context for the dns_cache object */ |
70 | | isc_mem_t *hmctx; /* Heap memory */ |
71 | | isc_mem_t *tmctx; /* Tree memory */ |
72 | | char *name; |
73 | | isc_refcount_t references; |
74 | | |
75 | | /* Locked by 'lock'. */ |
76 | | dns_rdataclass_t rdclass; |
77 | | dns_db_t *db; |
78 | | size_t size; |
79 | | dns_ttl_t serve_stale_ttl; |
80 | | dns_ttl_t serve_stale_refresh; |
81 | | isc_stats_t *stats; |
82 | | uint32_t maxrrperset; |
83 | | uint32_t maxtypepername; |
84 | | }; |
85 | | |
86 | | /*** |
87 | | *** Functions |
88 | | ***/ |
89 | | |
90 | | static isc_result_t |
91 | | cache_create_db(dns_cache_t *cache, dns_db_t **dbp, isc_mem_t **tmctxp, |
92 | 0 | isc_mem_t **hmctxp) { |
93 | 0 | isc_result_t result; |
94 | 0 | char *argv[1] = { 0 }; |
95 | 0 | dns_db_t *db = NULL; |
96 | 0 | isc_mem_t *tmctx = NULL, *hmctx = NULL; |
97 | | |
98 | | /* |
99 | | * This will be the cache memory context, which is subject |
100 | | * to cleaning when the configured memory limits are exceeded. |
101 | | */ |
102 | 0 | isc_mem_create("cache", &tmctx); |
103 | | |
104 | | /* |
105 | | * This will be passed to RBTDB to use for heaps. This is separate |
106 | | * from the main cache memory because it can grow quite large under |
107 | | * heavy load and could otherwise cause the cache to be cleaned too |
108 | | * aggressively. |
109 | | */ |
110 | 0 | isc_mem_create("cache_heap", &hmctx); |
111 | | |
112 | | /* |
113 | | * For databases of type "qpcache" or "rbt" (which are the |
114 | | * only cache implementations currently in existence) we pass |
115 | | * hmctx to dns_db_create() via argv[0]. |
116 | | */ |
117 | 0 | argv[0] = (char *)hmctx; |
118 | 0 | result = dns_db_create(tmctx, CACHEDB_DEFAULT, dns_rootname, |
119 | 0 | dns_dbtype_cache, cache->rdclass, 1, argv, &db); |
120 | 0 | if (result != ISC_R_SUCCESS) { |
121 | 0 | goto cleanup_mctx; |
122 | 0 | } |
123 | 0 | result = dns_db_setcachestats(db, cache->stats); |
124 | 0 | if (result != ISC_R_SUCCESS) { |
125 | 0 | goto cleanup_db; |
126 | 0 | } |
127 | | |
128 | 0 | dns_db_setservestalettl(db, cache->serve_stale_ttl); |
129 | 0 | dns_db_setservestalerefresh(db, cache->serve_stale_refresh); |
130 | 0 | dns_db_setmaxrrperset(db, cache->maxrrperset); |
131 | 0 | dns_db_setmaxtypepername(db, cache->maxtypepername); |
132 | | |
133 | | /* |
134 | | * XXX this is only used by the RBT cache, and can |
135 | | * be removed when it is. |
136 | | */ |
137 | 0 | dns_db_setloop(db, isc_loop_main()); |
138 | |
|
139 | 0 | *dbp = db; |
140 | 0 | *hmctxp = hmctx; |
141 | 0 | *tmctxp = tmctx; |
142 | |
|
143 | 0 | return ISC_R_SUCCESS; |
144 | | |
145 | 0 | cleanup_db: |
146 | 0 | dns_db_detach(&db); |
147 | 0 | cleanup_mctx: |
148 | 0 | isc_mem_detach(&hmctx); |
149 | 0 | isc_mem_detach(&tmctx); |
150 | |
|
151 | 0 | return result; |
152 | 0 | } |
153 | | |
154 | | static void |
155 | 0 | cache_destroy(dns_cache_t *cache) { |
156 | 0 | isc_stats_detach(&cache->stats); |
157 | 0 | isc_mutex_destroy(&cache->lock); |
158 | 0 | isc_mem_free(cache->mctx, cache->name); |
159 | 0 | if (cache->hmctx != NULL) { |
160 | 0 | isc_mem_detach(&cache->hmctx); |
161 | 0 | } |
162 | 0 | if (cache->tmctx != NULL) { |
163 | 0 | isc_mem_detach(&cache->tmctx); |
164 | 0 | } |
165 | 0 | isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache)); |
166 | 0 | } |
167 | | |
168 | | isc_result_t |
169 | | dns_cache_create(dns_rdataclass_t rdclass, const char *cachename, |
170 | 0 | isc_mem_t *mctx, dns_cache_t **cachep) { |
171 | 0 | isc_result_t result; |
172 | 0 | dns_cache_t *cache = NULL; |
173 | |
|
174 | 0 | REQUIRE(cachename != NULL); |
175 | 0 | REQUIRE(cachep != NULL && *cachep == NULL); |
176 | |
|
177 | 0 | cache = isc_mem_get(mctx, sizeof(*cache)); |
178 | 0 | *cache = (dns_cache_t){ |
179 | 0 | .rdclass = rdclass, |
180 | 0 | .name = isc_mem_strdup(mctx, cachename), |
181 | 0 | .references = ISC_REFCOUNT_INITIALIZER(1), |
182 | 0 | .magic = CACHE_MAGIC, |
183 | 0 | }; |
184 | |
|
185 | 0 | isc_mutex_init(&cache->lock); |
186 | 0 | isc_mem_attach(mctx, &cache->mctx); |
187 | |
|
188 | 0 | isc_stats_create(mctx, &cache->stats, dns_cachestatscounter_max); |
189 | | |
190 | | /* |
191 | | * Create the database |
192 | | */ |
193 | 0 | result = cache_create_db(cache, &cache->db, &cache->tmctx, |
194 | 0 | &cache->hmctx); |
195 | 0 | if (result != ISC_R_SUCCESS) { |
196 | 0 | goto cleanup; |
197 | 0 | } |
198 | | |
199 | 0 | *cachep = cache; |
200 | 0 | return ISC_R_SUCCESS; |
201 | | |
202 | 0 | cleanup: |
203 | 0 | cache_destroy(cache); |
204 | 0 | return result; |
205 | 0 | } |
206 | | |
207 | | static void |
208 | 0 | cache_cleanup(dns_cache_t *cache) { |
209 | 0 | REQUIRE(VALID_CACHE(cache)); |
210 | |
|
211 | 0 | isc_refcount_destroy(&cache->references); |
212 | 0 | cache->magic = 0; |
213 | |
|
214 | 0 | isc_mem_clearwater(cache->tmctx); |
215 | 0 | dns_db_detach(&cache->db); |
216 | |
|
217 | 0 | cache_destroy(cache); |
218 | 0 | } |
219 | | |
220 | | #if DNS_CACHE_TRACE |
221 | | ISC_REFCOUNT_TRACE_IMPL(dns_cache, cache_cleanup); |
222 | | #else |
223 | | ISC_REFCOUNT_IMPL(dns_cache, cache_cleanup); |
224 | | #endif |
225 | | |
226 | | void |
227 | 0 | dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) { |
228 | 0 | REQUIRE(VALID_CACHE(cache)); |
229 | 0 | REQUIRE(dbp != NULL && *dbp == NULL); |
230 | 0 | REQUIRE(cache->db != NULL); |
231 | |
|
232 | 0 | LOCK(&cache->lock); |
233 | 0 | dns_db_attach(cache->db, dbp); |
234 | 0 | UNLOCK(&cache->lock); |
235 | 0 | } |
236 | | |
237 | | const char * |
238 | 0 | dns_cache_getname(dns_cache_t *cache) { |
239 | 0 | REQUIRE(VALID_CACHE(cache)); |
240 | |
|
241 | 0 | return cache->name; |
242 | 0 | } |
243 | | |
244 | | static void |
245 | 0 | updatewater(dns_cache_t *cache) { |
246 | 0 | size_t hi = cache->size - (cache->size >> 3); /* ~ 7/8ths. */ |
247 | 0 | size_t lo = cache->size - (cache->size >> 2); /* ~ 3/4ths. */ |
248 | 0 | if (cache->size == 0U || hi == 0U || lo == 0U) { |
249 | 0 | isc_mem_clearwater(cache->tmctx); |
250 | 0 | } else { |
251 | 0 | isc_mem_setwater(cache->tmctx, hi, lo); |
252 | 0 | } |
253 | 0 | } |
254 | | |
255 | | void |
256 | 0 | dns_cache_setcachesize(dns_cache_t *cache, size_t size) { |
257 | 0 | REQUIRE(VALID_CACHE(cache)); |
258 | | |
259 | | /* |
260 | | * Impose a minimum cache size; pathological things happen if there |
261 | | * is too little room. |
262 | | */ |
263 | 0 | if (size != 0U && size < DNS_CACHE_MINSIZE) { |
264 | 0 | size = DNS_CACHE_MINSIZE; |
265 | 0 | } |
266 | |
|
267 | 0 | LOCK(&cache->lock); |
268 | 0 | cache->size = size; |
269 | 0 | updatewater(cache); |
270 | 0 | UNLOCK(&cache->lock); |
271 | 0 | } |
272 | | |
273 | | size_t |
274 | 0 | dns_cache_getcachesize(dns_cache_t *cache) { |
275 | 0 | size_t size; |
276 | |
|
277 | 0 | REQUIRE(VALID_CACHE(cache)); |
278 | |
|
279 | 0 | LOCK(&cache->lock); |
280 | 0 | size = cache->size; |
281 | 0 | UNLOCK(&cache->lock); |
282 | |
|
283 | 0 | return size; |
284 | 0 | } |
285 | | |
286 | | void |
287 | 0 | dns_cache_setservestalettl(dns_cache_t *cache, dns_ttl_t ttl) { |
288 | 0 | REQUIRE(VALID_CACHE(cache)); |
289 | |
|
290 | 0 | LOCK(&cache->lock); |
291 | 0 | cache->serve_stale_ttl = ttl; |
292 | 0 | UNLOCK(&cache->lock); |
293 | |
|
294 | 0 | (void)dns_db_setservestalettl(cache->db, ttl); |
295 | 0 | } |
296 | | |
297 | | dns_ttl_t |
298 | 0 | dns_cache_getservestalettl(dns_cache_t *cache) { |
299 | 0 | dns_ttl_t ttl; |
300 | 0 | isc_result_t result; |
301 | |
|
302 | 0 | REQUIRE(VALID_CACHE(cache)); |
303 | | |
304 | | /* |
305 | | * Could get it straight from the dns_cache_t, but use db |
306 | | * to confirm the value that the db is really using. |
307 | | */ |
308 | 0 | result = dns_db_getservestalettl(cache->db, &ttl); |
309 | 0 | return result == ISC_R_SUCCESS ? ttl : 0; |
310 | 0 | } |
311 | | |
312 | | void |
313 | 0 | dns_cache_setservestalerefresh(dns_cache_t *cache, dns_ttl_t interval) { |
314 | 0 | REQUIRE(VALID_CACHE(cache)); |
315 | |
|
316 | 0 | LOCK(&cache->lock); |
317 | 0 | cache->serve_stale_refresh = interval; |
318 | 0 | UNLOCK(&cache->lock); |
319 | |
|
320 | 0 | (void)dns_db_setservestalerefresh(cache->db, interval); |
321 | 0 | } |
322 | | |
323 | | dns_ttl_t |
324 | 0 | dns_cache_getservestalerefresh(dns_cache_t *cache) { |
325 | 0 | isc_result_t result; |
326 | 0 | dns_ttl_t interval; |
327 | |
|
328 | 0 | REQUIRE(VALID_CACHE(cache)); |
329 | |
|
330 | 0 | result = dns_db_getservestalerefresh(cache->db, &interval); |
331 | 0 | return result == ISC_R_SUCCESS ? interval : 0; |
332 | 0 | } |
333 | | |
334 | | isc_result_t |
335 | 0 | dns_cache_flush(dns_cache_t *cache) { |
336 | 0 | dns_db_t *db = NULL, *olddb; |
337 | 0 | isc_mem_t *tmctx = NULL, *oldtmctx; |
338 | 0 | isc_mem_t *hmctx = NULL, *oldhmctx; |
339 | 0 | isc_result_t result; |
340 | |
|
341 | 0 | result = cache_create_db(cache, &db, &tmctx, &hmctx); |
342 | 0 | if (result != ISC_R_SUCCESS) { |
343 | 0 | return result; |
344 | 0 | } |
345 | | |
346 | 0 | LOCK(&cache->lock); |
347 | 0 | isc_mem_clearwater(cache->tmctx); |
348 | 0 | oldhmctx = cache->hmctx; |
349 | 0 | cache->hmctx = hmctx; |
350 | 0 | oldtmctx = cache->tmctx; |
351 | 0 | cache->tmctx = tmctx; |
352 | 0 | updatewater(cache); |
353 | 0 | olddb = cache->db; |
354 | 0 | cache->db = db; |
355 | 0 | UNLOCK(&cache->lock); |
356 | |
|
357 | 0 | dns_db_detach(&olddb); |
358 | 0 | isc_mem_detach(&oldhmctx); |
359 | 0 | isc_mem_detach(&oldtmctx); |
360 | |
|
361 | 0 | return ISC_R_SUCCESS; |
362 | 0 | } |
363 | | |
364 | | static isc_result_t |
365 | 0 | clearnode(dns_db_t *db, dns_dbnode_t *node) { |
366 | 0 | isc_result_t result; |
367 | 0 | dns_rdatasetiter_t *iter = NULL; |
368 | |
|
369 | 0 | result = dns_db_allrdatasets(db, node, NULL, DNS_DB_STALEOK, |
370 | 0 | (isc_stdtime_t)0, &iter); |
371 | 0 | if (result != ISC_R_SUCCESS) { |
372 | 0 | return result; |
373 | 0 | } |
374 | | |
375 | 0 | DNS_RDATASETITER_FOREACH (iter) { |
376 | 0 | dns_rdataset_t rdataset = DNS_RDATASET_INIT; |
377 | |
|
378 | 0 | dns_rdatasetiter_current(iter, &rdataset); |
379 | 0 | result = dns_db_deleterdataset(db, node, NULL, rdataset.type, |
380 | 0 | rdataset.covers); |
381 | 0 | dns_rdataset_disassociate(&rdataset); |
382 | 0 | if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) { |
383 | 0 | break; |
384 | 0 | } |
385 | 0 | } |
386 | |
|
387 | 0 | dns_rdatasetiter_destroy(&iter); |
388 | 0 | return result; |
389 | 0 | } |
390 | | |
391 | | static isc_result_t |
392 | 0 | cleartree(dns_db_t *db, const dns_name_t *name) { |
393 | 0 | isc_result_t result, answer = ISC_R_SUCCESS; |
394 | 0 | dns_dbiterator_t *iter = NULL; |
395 | 0 | dns_dbnode_t *node = NULL, *top = NULL; |
396 | 0 | dns_fixedname_t fnodename; |
397 | 0 | dns_name_t *nodename; |
398 | | |
399 | | /* |
400 | | * Create the node if it doesn't exist so dns_dbiterator_seek() |
401 | | * can find it. We will continue even if this fails. |
402 | | */ |
403 | 0 | (void)dns_db_findnode(db, name, true, &top); |
404 | |
|
405 | 0 | nodename = dns_fixedname_initname(&fnodename); |
406 | |
|
407 | 0 | result = dns_db_createiterator(db, 0, &iter); |
408 | 0 | if (result != ISC_R_SUCCESS) { |
409 | 0 | goto cleanup; |
410 | 0 | } |
411 | | |
412 | 0 | result = dns_dbiterator_seek(iter, name); |
413 | 0 | if (result == DNS_R_PARTIALMATCH) { |
414 | 0 | result = dns_dbiterator_next(iter); |
415 | 0 | } |
416 | 0 | if (result != ISC_R_SUCCESS) { |
417 | 0 | goto cleanup; |
418 | 0 | } |
419 | | |
420 | 0 | while (result == ISC_R_SUCCESS) { |
421 | 0 | result = dns_dbiterator_current(iter, &node, nodename); |
422 | 0 | if (result == DNS_R_NEWORIGIN) { |
423 | 0 | result = ISC_R_SUCCESS; |
424 | 0 | } |
425 | 0 | if (result != ISC_R_SUCCESS) { |
426 | 0 | goto cleanup; |
427 | 0 | } |
428 | | /* |
429 | | * Are we done? |
430 | | */ |
431 | 0 | if (!dns_name_issubdomain(nodename, name)) { |
432 | 0 | goto cleanup; |
433 | 0 | } |
434 | | |
435 | | /* |
436 | | * If clearnode fails record and move onto the next node. |
437 | | */ |
438 | 0 | result = clearnode(db, node); |
439 | 0 | if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) { |
440 | 0 | answer = result; |
441 | 0 | } |
442 | 0 | dns_db_detachnode(db, &node); |
443 | 0 | result = dns_dbiterator_next(iter); |
444 | 0 | } |
445 | | |
446 | 0 | cleanup: |
447 | 0 | if (result == ISC_R_NOMORE || result == ISC_R_NOTFOUND) { |
448 | 0 | result = ISC_R_SUCCESS; |
449 | 0 | } |
450 | 0 | if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) { |
451 | 0 | answer = result; |
452 | 0 | } |
453 | 0 | if (node != NULL) { |
454 | 0 | dns_db_detachnode(db, &node); |
455 | 0 | } |
456 | 0 | if (iter != NULL) { |
457 | 0 | dns_dbiterator_destroy(&iter); |
458 | 0 | } |
459 | 0 | if (top != NULL) { |
460 | 0 | dns_db_detachnode(db, &top); |
461 | 0 | } |
462 | |
|
463 | 0 | return answer; |
464 | 0 | } |
465 | | |
466 | | isc_result_t |
467 | 0 | dns_cache_flushname(dns_cache_t *cache, const dns_name_t *name) { |
468 | 0 | return dns_cache_flushnode(cache, name, false); |
469 | 0 | } |
470 | | |
471 | | isc_result_t |
472 | 0 | dns_cache_flushnode(dns_cache_t *cache, const dns_name_t *name, bool tree) { |
473 | 0 | isc_result_t result; |
474 | 0 | dns_dbnode_t *node = NULL; |
475 | 0 | dns_db_t *db = NULL; |
476 | |
|
477 | 0 | if (tree && dns_name_equal(name, dns_rootname)) { |
478 | 0 | return dns_cache_flush(cache); |
479 | 0 | } |
480 | | |
481 | 0 | LOCK(&cache->lock); |
482 | 0 | if (cache->db != NULL) { |
483 | 0 | dns_db_attach(cache->db, &db); |
484 | 0 | } |
485 | 0 | UNLOCK(&cache->lock); |
486 | 0 | if (db == NULL) { |
487 | 0 | return ISC_R_SUCCESS; |
488 | 0 | } |
489 | | |
490 | 0 | if (tree) { |
491 | 0 | result = cleartree(cache->db, name); |
492 | 0 | } else { |
493 | 0 | result = dns_db_findnode(cache->db, name, false, &node); |
494 | 0 | if (result == ISC_R_NOTFOUND) { |
495 | 0 | result = ISC_R_SUCCESS; |
496 | 0 | goto cleanup_db; |
497 | 0 | } |
498 | 0 | if (result != ISC_R_SUCCESS) { |
499 | 0 | goto cleanup_db; |
500 | 0 | } |
501 | 0 | result = clearnode(cache->db, node); |
502 | 0 | dns_db_detachnode(cache->db, &node); |
503 | 0 | } |
504 | | |
505 | 0 | cleanup_db: |
506 | 0 | dns_db_detach(&db); |
507 | 0 | return result; |
508 | 0 | } |
509 | | |
510 | | isc_stats_t * |
511 | 0 | dns_cache_getstats(dns_cache_t *cache) { |
512 | 0 | REQUIRE(VALID_CACHE(cache)); |
513 | 0 | return cache->stats; |
514 | 0 | } |
515 | | |
516 | | void |
517 | 0 | dns_cache_updatestats(dns_cache_t *cache, isc_result_t result) { |
518 | 0 | REQUIRE(VALID_CACHE(cache)); |
519 | 0 | if (cache->stats == NULL) { |
520 | 0 | return; |
521 | 0 | } |
522 | | |
523 | 0 | switch (result) { |
524 | 0 | case ISC_R_SUCCESS: |
525 | 0 | case DNS_R_NCACHENXDOMAIN: |
526 | 0 | case DNS_R_NCACHENXRRSET: |
527 | 0 | case DNS_R_CNAME: |
528 | 0 | case DNS_R_DNAME: |
529 | 0 | case DNS_R_GLUE: |
530 | 0 | case DNS_R_ZONECUT: |
531 | 0 | case DNS_R_COVERINGNSEC: |
532 | 0 | isc_stats_increment(cache->stats, |
533 | 0 | dns_cachestatscounter_queryhits); |
534 | 0 | break; |
535 | 0 | default: |
536 | 0 | isc_stats_increment(cache->stats, |
537 | 0 | dns_cachestatscounter_querymisses); |
538 | 0 | } |
539 | 0 | } |
540 | | |
541 | | void |
542 | 0 | dns_cache_setmaxrrperset(dns_cache_t *cache, uint32_t value) { |
543 | 0 | REQUIRE(VALID_CACHE(cache)); |
544 | |
|
545 | 0 | cache->maxrrperset = value; |
546 | 0 | if (cache->db != NULL) { |
547 | 0 | dns_db_setmaxrrperset(cache->db, value); |
548 | 0 | } |
549 | 0 | } |
550 | | |
551 | | void |
552 | 0 | dns_cache_setmaxtypepername(dns_cache_t *cache, uint32_t value) { |
553 | 0 | REQUIRE(VALID_CACHE(cache)); |
554 | |
|
555 | 0 | cache->maxtypepername = value; |
556 | 0 | if (cache->db != NULL) { |
557 | 0 | dns_db_setmaxtypepername(cache->db, value); |
558 | 0 | } |
559 | 0 | } |
560 | | |
561 | | /* |
562 | | * XXX: Much of the following code has been copied in from statschannel.c. |
563 | | * We should refactor this into a generic function in stats.c that can be |
564 | | * called from both places. |
565 | | */ |
566 | | typedef struct cache_dumparg { |
567 | | isc_statsformat_t type; |
568 | | void *arg; /* type dependent argument */ |
569 | | int ncounters; /* for general statistics */ |
570 | | int *counterindices; /* for general statistics */ |
571 | | uint64_t *countervalues; /* for general statistics */ |
572 | | isc_result_t result; |
573 | | } cache_dumparg_t; |
574 | | |
575 | | static void |
576 | 0 | getcounter(isc_statscounter_t counter, uint64_t val, void *arg) { |
577 | 0 | cache_dumparg_t *dumparg = arg; |
578 | |
|
579 | 0 | REQUIRE(counter < dumparg->ncounters); |
580 | 0 | dumparg->countervalues[counter] = val; |
581 | 0 | } |
582 | | |
583 | | static void |
584 | | getcounters(isc_stats_t *stats, isc_statsformat_t type, int ncounters, |
585 | 0 | int *indices, uint64_t *values) { |
586 | 0 | cache_dumparg_t dumparg; |
587 | |
|
588 | 0 | memset(values, 0, sizeof(values[0]) * ncounters); |
589 | |
|
590 | 0 | dumparg.type = type; |
591 | 0 | dumparg.ncounters = ncounters; |
592 | 0 | dumparg.counterindices = indices; |
593 | 0 | dumparg.countervalues = values; |
594 | |
|
595 | 0 | isc_stats_dump(stats, getcounter, &dumparg, ISC_STATSDUMP_VERBOSE); |
596 | 0 | } |
597 | | |
598 | | void |
599 | 0 | dns_cache_dumpstats(dns_cache_t *cache, FILE *fp) { |
600 | 0 | int indices[dns_cachestatscounter_max]; |
601 | 0 | uint64_t values[dns_cachestatscounter_max]; |
602 | |
|
603 | 0 | REQUIRE(VALID_CACHE(cache)); |
604 | |
|
605 | 0 | getcounters(cache->stats, isc_statsformat_file, |
606 | 0 | dns_cachestatscounter_max, indices, values); |
607 | |
|
608 | 0 | fprintf(fp, "%20" PRIu64 " %s\n", values[dns_cachestatscounter_hits], |
609 | 0 | "cache hits"); |
610 | 0 | fprintf(fp, "%20" PRIu64 " %s\n", values[dns_cachestatscounter_misses], |
611 | 0 | "cache misses"); |
612 | 0 | fprintf(fp, "%20" PRIu64 " %s\n", |
613 | 0 | values[dns_cachestatscounter_queryhits], |
614 | 0 | "cache hits (from query)"); |
615 | 0 | fprintf(fp, "%20" PRIu64 " %s\n", |
616 | 0 | values[dns_cachestatscounter_querymisses], |
617 | 0 | "cache misses (from query)"); |
618 | 0 | fprintf(fp, "%20" PRIu64 " %s\n", |
619 | 0 | values[dns_cachestatscounter_deletelru], |
620 | 0 | "cache records deleted due to memory exhaustion"); |
621 | 0 | fprintf(fp, "%20" PRIu64 " %s\n", |
622 | 0 | values[dns_cachestatscounter_deletettl], |
623 | 0 | "cache records deleted due to TTL expiration"); |
624 | 0 | fprintf(fp, "%20" PRIu64 " %s\n", |
625 | 0 | values[dns_cachestatscounter_coveringnsec], |
626 | 0 | "covering nsec returned"); |
627 | 0 | fprintf(fp, "%20u %s\n", dns_db_nodecount(cache->db, dns_dbtree_main), |
628 | 0 | "cache database nodes"); |
629 | 0 | fprintf(fp, "%20u %s\n", dns_db_nodecount(cache->db, dns_dbtree_nsec), |
630 | 0 | "cache NSEC auxiliary database nodes"); |
631 | 0 | fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)dns_db_hashsize(cache->db), |
632 | 0 | "cache database hash buckets"); |
633 | |
|
634 | 0 | fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_inuse(cache->tmctx), |
635 | 0 | "cache tree memory in use"); |
636 | |
|
637 | 0 | fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_inuse(cache->hmctx), |
638 | 0 | "cache heap memory in use"); |
639 | 0 | } |
640 | | |
641 | | #ifdef HAVE_LIBXML2 |
642 | | #define TRY0(a) \ |
643 | | do { \ |
644 | | xmlrc = (a); \ |
645 | | if (xmlrc < 0) \ |
646 | | goto error; \ |
647 | | } while (0) |
648 | | static int |
649 | | renderstat(const char *name, uint64_t value, xmlTextWriterPtr writer) { |
650 | | int xmlrc; |
651 | | |
652 | | TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter")); |
653 | | TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name", |
654 | | ISC_XMLCHAR name)); |
655 | | TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", value)); |
656 | | TRY0(xmlTextWriterEndElement(writer)); /* counter */ |
657 | | |
658 | | error: |
659 | | return xmlrc; |
660 | | } |
661 | | |
662 | | int |
663 | | dns_cache_renderxml(dns_cache_t *cache, void *writer0) { |
664 | | int indices[dns_cachestatscounter_max]; |
665 | | uint64_t values[dns_cachestatscounter_max]; |
666 | | int xmlrc; |
667 | | xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0; |
668 | | |
669 | | REQUIRE(VALID_CACHE(cache)); |
670 | | |
671 | | getcounters(cache->stats, isc_statsformat_file, |
672 | | dns_cachestatscounter_max, indices, values); |
673 | | TRY0(renderstat("CacheHits", values[dns_cachestatscounter_hits], |
674 | | writer)); |
675 | | TRY0(renderstat("CacheMisses", values[dns_cachestatscounter_misses], |
676 | | writer)); |
677 | | TRY0(renderstat("QueryHits", values[dns_cachestatscounter_queryhits], |
678 | | writer)); |
679 | | TRY0(renderstat("QueryMisses", |
680 | | values[dns_cachestatscounter_querymisses], writer)); |
681 | | TRY0(renderstat("DeleteLRU", values[dns_cachestatscounter_deletelru], |
682 | | writer)); |
683 | | TRY0(renderstat("DeleteTTL", values[dns_cachestatscounter_deletettl], |
684 | | writer)); |
685 | | TRY0(renderstat("CoveringNSEC", |
686 | | values[dns_cachestatscounter_coveringnsec], writer)); |
687 | | |
688 | | TRY0(renderstat("CacheNodes", |
689 | | dns_db_nodecount(cache->db, dns_dbtree_main), writer)); |
690 | | TRY0(renderstat("CacheNSECNodes", |
691 | | dns_db_nodecount(cache->db, dns_dbtree_nsec), writer)); |
692 | | TRY0(renderstat("CacheBuckets", dns_db_hashsize(cache->db), writer)); |
693 | | |
694 | | TRY0(renderstat("TreeMemInUse", isc_mem_inuse(cache->tmctx), writer)); |
695 | | |
696 | | TRY0(renderstat("HeapMemInUse", isc_mem_inuse(cache->hmctx), writer)); |
697 | | error: |
698 | | return xmlrc; |
699 | | } |
700 | | #endif /* ifdef HAVE_LIBXML2 */ |
701 | | |
702 | | #ifdef HAVE_JSON_C |
703 | | #define CHECKMEM(m) \ |
704 | | do { \ |
705 | | if (m == NULL) { \ |
706 | | result = ISC_R_NOMEMORY; \ |
707 | | goto error; \ |
708 | | } \ |
709 | | } while (0) |
710 | | |
711 | | isc_result_t |
712 | | dns_cache_renderjson(dns_cache_t *cache, void *cstats0) { |
713 | | isc_result_t result = ISC_R_SUCCESS; |
714 | | int indices[dns_cachestatscounter_max]; |
715 | | uint64_t values[dns_cachestatscounter_max]; |
716 | | json_object *obj; |
717 | | json_object *cstats = (json_object *)cstats0; |
718 | | |
719 | | REQUIRE(VALID_CACHE(cache)); |
720 | | |
721 | | getcounters(cache->stats, isc_statsformat_file, |
722 | | dns_cachestatscounter_max, indices, values); |
723 | | |
724 | | obj = json_object_new_int64(values[dns_cachestatscounter_hits]); |
725 | | CHECKMEM(obj); |
726 | | json_object_object_add(cstats, "CacheHits", obj); |
727 | | |
728 | | obj = json_object_new_int64(values[dns_cachestatscounter_misses]); |
729 | | CHECKMEM(obj); |
730 | | json_object_object_add(cstats, "CacheMisses", obj); |
731 | | |
732 | | obj = json_object_new_int64(values[dns_cachestatscounter_queryhits]); |
733 | | CHECKMEM(obj); |
734 | | json_object_object_add(cstats, "QueryHits", obj); |
735 | | |
736 | | obj = json_object_new_int64(values[dns_cachestatscounter_querymisses]); |
737 | | CHECKMEM(obj); |
738 | | json_object_object_add(cstats, "QueryMisses", obj); |
739 | | |
740 | | obj = json_object_new_int64(values[dns_cachestatscounter_deletelru]); |
741 | | CHECKMEM(obj); |
742 | | json_object_object_add(cstats, "DeleteLRU", obj); |
743 | | |
744 | | obj = json_object_new_int64(values[dns_cachestatscounter_deletettl]); |
745 | | CHECKMEM(obj); |
746 | | json_object_object_add(cstats, "DeleteTTL", obj); |
747 | | |
748 | | obj = json_object_new_int64(values[dns_cachestatscounter_coveringnsec]); |
749 | | CHECKMEM(obj); |
750 | | json_object_object_add(cstats, "CoveringNSEC", obj); |
751 | | |
752 | | obj = json_object_new_int64( |
753 | | dns_db_nodecount(cache->db, dns_dbtree_main)); |
754 | | CHECKMEM(obj); |
755 | | json_object_object_add(cstats, "CacheNodes", obj); |
756 | | |
757 | | obj = json_object_new_int64( |
758 | | dns_db_nodecount(cache->db, dns_dbtree_nsec)); |
759 | | CHECKMEM(obj); |
760 | | json_object_object_add(cstats, "CacheNSECNodes", obj); |
761 | | |
762 | | obj = json_object_new_int64(dns_db_hashsize(cache->db)); |
763 | | CHECKMEM(obj); |
764 | | json_object_object_add(cstats, "CacheBuckets", obj); |
765 | | |
766 | | obj = json_object_new_int64(isc_mem_inuse(cache->tmctx)); |
767 | | CHECKMEM(obj); |
768 | | json_object_object_add(cstats, "TreeMemInUse", obj); |
769 | | |
770 | | obj = json_object_new_int64(isc_mem_inuse(cache->hmctx)); |
771 | | CHECKMEM(obj); |
772 | | json_object_object_add(cstats, "HeapMemInUse", obj); |
773 | | |
774 | | result = ISC_R_SUCCESS; |
775 | | error: |
776 | | return result; |
777 | | } |
778 | | #endif /* ifdef HAVE_JSON_C */ |