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