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