/src/bind9/lib/dns/qpcache.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 <stdalign.h> |
18 | | #include <stdbool.h> |
19 | | |
20 | | #include <isc/ascii.h> |
21 | | #include <isc/async.h> |
22 | | #include <isc/atomic.h> |
23 | | #include <isc/file.h> |
24 | | #include <isc/heap.h> |
25 | | #include <isc/hex.h> |
26 | | #include <isc/log.h> |
27 | | #include <isc/loop.h> |
28 | | #include <isc/mem.h> |
29 | | #include <isc/mutex.h> |
30 | | #include <isc/os.h> |
31 | | #include <isc/queue.h> |
32 | | #include <isc/random.h> |
33 | | #include <isc/refcount.h> |
34 | | #include <isc/result.h> |
35 | | #include <isc/rwlock.h> |
36 | | #include <isc/sieve.h> |
37 | | #include <isc/stdio.h> |
38 | | #include <isc/string.h> |
39 | | #include <isc/time.h> |
40 | | #include <isc/urcu.h> |
41 | | #include <isc/util.h> |
42 | | |
43 | | #include <dns/callbacks.h> |
44 | | #include <dns/db.h> |
45 | | #include <dns/dbiterator.h> |
46 | | #include <dns/fixedname.h> |
47 | | #include <dns/masterdump.h> |
48 | | #include <dns/nsec.h> |
49 | | #include <dns/qp.h> |
50 | | #include <dns/rdata.h> |
51 | | #include <dns/rdataset.h> |
52 | | #include <dns/rdatasetiter.h> |
53 | | #include <dns/rdataslab.h> |
54 | | #include <dns/rdatastruct.h> |
55 | | #include <dns/rdatatype.h> |
56 | | #include <dns/stats.h> |
57 | | #include <dns/time.h> |
58 | | #include <dns/types.h> |
59 | | #include <dns/view.h> |
60 | | |
61 | | #include "db_p.h" |
62 | | #include "qpcache_p.h" |
63 | | #include "rdataslab_p.h" |
64 | | |
65 | | #ifndef DNS_QPCACHE_LOG_STATS_LEVEL |
66 | | #define DNS_QPCACHE_LOG_STATS_LEVEL 3 |
67 | | #endif |
68 | | |
69 | | #define CHECK(op) \ |
70 | | do { \ |
71 | | result = (op); \ |
72 | | if (result != ISC_R_SUCCESS) \ |
73 | | goto failure; \ |
74 | | } while (0) |
75 | | |
76 | | #define STALE_TTL(header, qpdb) \ |
77 | 0 | (NXDOMAIN(header) ? 0 : qpdb->common.serve_stale_ttl) |
78 | | |
79 | | #define ACTIVE(header, now) \ |
80 | 0 | (((header)->expire > (now)) || \ |
81 | 0 | ((header)->expire == (now) && ZEROTTL(header))) |
82 | | |
83 | | #define EXPIREDOK(iterator) \ |
84 | 0 | (((iterator)->common.options & DNS_DB_EXPIREDOK) != 0) |
85 | | |
86 | 0 | #define STALEOK(iterator) (((iterator)->common.options & DNS_DB_STALEOK) != 0) |
87 | | |
88 | 0 | #define KEEPSTALE(qpdb) ((qpdb)->common.serve_stale_ttl > 0) |
89 | | |
90 | | /*% |
91 | | * Note that "impmagic" is not the first four bytes of the struct, so |
92 | | * ISC_MAGIC_VALID cannot be used. |
93 | | */ |
94 | 0 | #define QPDB_MAGIC ISC_MAGIC('Q', 'P', 'D', '4') |
95 | | #define VALID_QPDB(qpdb) \ |
96 | | ((qpdb) != NULL && (qpdb)->common.impmagic == QPDB_MAGIC) |
97 | | |
98 | 0 | #define HEADERNODE(h) ((qpcnode_t *)((h)->node)) |
99 | | |
100 | | /* |
101 | | * Allow clients with a virtual time of up to 10 seconds in the past to see |
102 | | * records that would have otherwise have expired. |
103 | | */ |
104 | 0 | #define QPDB_VIRTUAL 10 |
105 | | |
106 | | /* |
107 | | * This defines the number of headers that we try to expire each time the |
108 | | * expire_ttl_headers() is run. The number should be small enough, so the |
109 | | * TTL-based header expiration doesn't take too long, but it should be large |
110 | | * enough, so we expire enough headers if their TTL is clustered. |
111 | | */ |
112 | 0 | #define DNS_QPDB_EXPIRE_TTL_COUNT 10 |
113 | | |
114 | | /*% |
115 | | * Forward declarations |
116 | | */ |
117 | | typedef struct qpcache qpcache_t; |
118 | | |
119 | | /*% |
120 | | * This is the structure that is used for each node in the qp trie of |
121 | | * trees. |
122 | | */ |
123 | | typedef struct qpcnode qpcnode_t; |
124 | | struct qpcnode { |
125 | | DBNODE_FIELDS; |
126 | | |
127 | | qpcache_t *qpdb; |
128 | | |
129 | | uint8_t : 0; |
130 | | unsigned int delegating : 1; |
131 | | unsigned int nspace : 2; /*%< range is 0..3 */ |
132 | | unsigned int havensec : 1; |
133 | | uint8_t : 0; |
134 | | |
135 | | /* |
136 | | * 'erefs' counts external references held by a caller: for |
137 | | * example, it could be incremented by dns_db_findnode(), |
138 | | * and decremented by dns_db_detachnode(). |
139 | | * |
140 | | * 'references' counts internal references to the node object, |
141 | | * including the one held by the QP trie so the node won't be |
142 | | * deleted while it's quiescently stored in the database - even |
143 | | * though 'erefs' may be zero because no external caller is |
144 | | * using it at the time. |
145 | | * |
146 | | * Generally when 'erefs' is incremented or decremented, |
147 | | * 'references' is too. When both go to zero (meaning callers |
148 | | * and the database have both released the object) the object |
149 | | * is freed. |
150 | | * |
151 | | * Whenever 'erefs' is incremented from zero, we also aquire a |
152 | | * node use reference (see 'qpcache->references' below), and |
153 | | * release it when 'erefs' goes back to zero. This prevents the |
154 | | * database from being shut down until every caller has released |
155 | | * all nodes. |
156 | | */ |
157 | | isc_refcount_t references; |
158 | | isc_refcount_t erefs; |
159 | | |
160 | | struct cds_list_head types_list; |
161 | | struct cds_list_head *data; |
162 | | |
163 | | /*% |
164 | | * NOTE: The 'dirty' flag is protected by the node lock, so |
165 | | * this bitfield has to be separated from the one above. |
166 | | * We don't want it to share the same qword with bits |
167 | | * that can be accessed without the node lock. |
168 | | */ |
169 | | uint8_t : 0; |
170 | | uint8_t dirty : 1; |
171 | | uint8_t : 0; |
172 | | |
173 | | /*% |
174 | | * Used for dead nodes cleaning. This linked list is used to mark nodes |
175 | | * which have no data any longer, but we cannot unlink at that exact |
176 | | * moment because we did not or could not obtain a write lock on the |
177 | | * tree. |
178 | | */ |
179 | | isc_queue_node_t deadlink; |
180 | | }; |
181 | | |
182 | | /*% |
183 | | * One bucket structure will be created for each loop, and |
184 | | * nodes in the database will evenly distributed among buckets |
185 | | * to reduce contention between threads. |
186 | | */ |
187 | | typedef struct qpcache_bucket { |
188 | | /*% |
189 | | * Temporary storage for stale cache nodes and dynamically |
190 | | * deleted nodes that await being cleaned up. |
191 | | */ |
192 | | isc_queue_t deadnodes; |
193 | | |
194 | | /* Per-bucket lock. */ |
195 | | isc_rwlock_t lock; |
196 | | |
197 | | /* |
198 | | * The heap is used for TTL based expiry. Note that qpcache->hmctx |
199 | | * is the memory context to use for heap memory; this differs from |
200 | | * the main database memory context, which is qpcache->common.mctx. |
201 | | */ |
202 | | isc_heap_t *heap; |
203 | | |
204 | | /* SIEVE-LRU cache cleaning state. */ |
205 | | ISC_SIEVE(dns_slabtop_t) sieve; |
206 | | |
207 | | /* Padding to prevent false sharing between locks. */ |
208 | | uint8_t __padding[ISC_OS_CACHELINE_SIZE - |
209 | | (sizeof(isc_queue_t) + sizeof(isc_rwlock_t) + |
210 | | sizeof(isc_heap_t *) + |
211 | | sizeof(ISC_SIEVE(dns_slabtop_t))) % |
212 | | ISC_OS_CACHELINE_SIZE]; |
213 | | |
214 | | } qpcache_bucket_t; |
215 | | |
216 | | struct qpcache { |
217 | | /* Unlocked. */ |
218 | | dns_db_t common; |
219 | | /* Locks the data in this struct */ |
220 | | isc_rwlock_t lock; |
221 | | /* Locks the tree structure (prevents nodes appearing/disappearing) */ |
222 | | isc_rwlock_t tree_lock; |
223 | | |
224 | | /* |
225 | | * NOTE: 'references' is NOT the global reference counter for |
226 | | * the database object handled by dns_db_attach() and _detach(); |
227 | | * that one is 'common.references'. |
228 | | * |
229 | | * Instead, 'references' counts the number of nodes being used by |
230 | | * at least one external caller. (It's called 'references' to |
231 | | * leverage the ISC_REFCOUNT_STATIC macros, but 'nodes_in_use' |
232 | | * might be a clearer name.) |
233 | | * |
234 | | * One additional reference to this counter is held by the database |
235 | | * object itself. When 'common.references' goes to zero, that |
236 | | * reference is released. When in turn 'references' goes to zero, |
237 | | * the database is shut down and freed. |
238 | | */ |
239 | | isc_refcount_t references; |
240 | | |
241 | | dns_stats_t *rrsetstats; |
242 | | isc_stats_t *cachestats; |
243 | | |
244 | | uint32_t maxrrperset; /* Maximum RRs per RRset */ |
245 | | uint32_t maxtypepername; /* Maximum number of RR types per owner */ |
246 | | |
247 | | /* |
248 | | * The time after a failed lookup, where stale answers from cache |
249 | | * may be used directly in a DNS response without attempting a |
250 | | * new iterative lookup. |
251 | | */ |
252 | | uint32_t serve_stale_refresh; |
253 | | |
254 | | /* Locked by tree_lock. */ |
255 | | dns_qp_t *tree; |
256 | | |
257 | | isc_mem_t *hmctx; /* Memory context for the heaps */ |
258 | | |
259 | | size_t buckets_count; |
260 | | qpcache_bucket_t buckets[]; /* attribute((counted_by(buckets_count))) */ |
261 | | }; |
262 | | |
263 | | #ifdef DNS_DB_NODETRACE |
264 | | #define qpcache_ref(ptr) qpcache__ref(ptr, __func__, __FILE__, __LINE__) |
265 | | #define qpcache_unref(ptr) qpcache__unref(ptr, __func__, __FILE__, __LINE__) |
266 | | #define qpcache_attach(ptr, ptrp) \ |
267 | | qpcache__attach(ptr, ptrp, __func__, __FILE__, __LINE__) |
268 | | #define qpcache_detach(ptrp) qpcache__detach(ptrp, __func__, __FILE__, __LINE__) |
269 | | ISC_REFCOUNT_STATIC_TRACE_DECL(qpcache); |
270 | | #else |
271 | | ISC_REFCOUNT_STATIC_DECL(qpcache); |
272 | | #endif |
273 | | |
274 | | /*% |
275 | | * Search Context |
276 | | */ |
277 | | typedef struct { |
278 | | qpcache_t *qpdb; |
279 | | unsigned int options; |
280 | | dns_qpchain_t chain; |
281 | | dns_qpiter_t iter; |
282 | | bool need_cleanup; |
283 | | qpcnode_t *zonecut; |
284 | | dns_slabheader_t *zonecut_header; |
285 | | dns_slabheader_t *zonecut_sigheader; |
286 | | isc_stdtime_t now; |
287 | | } qpc_search_t; |
288 | | |
289 | | #ifdef DNS_DB_NODETRACE |
290 | | #define qpcnode_ref(ptr) qpcnode__ref(ptr, __func__, __FILE__, __LINE__) |
291 | | #define qpcnode_unref(ptr) qpcnode__unref(ptr, __func__, __FILE__, __LINE__) |
292 | | #define qpcnode_attach(ptr, ptrp) \ |
293 | | qpcnode__attach(ptr, ptrp, __func__, __FILE__, __LINE__) |
294 | | #define qpcnode_detach(ptrp) qpcnode__detach(ptrp, __func__, __FILE__, __LINE__) |
295 | | ISC_REFCOUNT_STATIC_TRACE_DECL(qpcnode); |
296 | | #else |
297 | | ISC_REFCOUNT_STATIC_DECL(qpcnode); |
298 | | #endif |
299 | | |
300 | | /* |
301 | | * Node methods forward declarations |
302 | | */ |
303 | | static void |
304 | | qpcnode_attachnode(dns_dbnode_t *source, dns_dbnode_t **targetp DNS__DB_FLARG); |
305 | | static void |
306 | | qpcnode_detachnode(dns_dbnode_t **nodep DNS__DB_FLARG); |
307 | | static void |
308 | | qpcnode_deletedata(dns_dbnode_t *node, void *data); |
309 | | static void |
310 | | qpcnode_expiredata(dns_dbnode_t *node, void *data); |
311 | | |
312 | | static dns_dbnode_methods_t qpcnode_methods = (dns_dbnode_methods_t){ |
313 | | .attachnode = qpcnode_attachnode, |
314 | | .detachnode = qpcnode_detachnode, |
315 | | .deletedata = qpcnode_deletedata, |
316 | | .expiredata = qpcnode_expiredata, |
317 | | }; |
318 | | |
319 | | /* QP methods */ |
320 | | static void |
321 | | qp_attach(void *uctx, void *pval, uint32_t ival); |
322 | | static void |
323 | | qp_detach(void *uctx, void *pval, uint32_t ival); |
324 | | static size_t |
325 | | qp_makekey(dns_qpkey_t key, void *uctx, void *pval, uint32_t ival); |
326 | | static void |
327 | | qp_triename(void *uctx, char *buf, size_t size); |
328 | | |
329 | | static dns_qpmethods_t qpmethods = { |
330 | | qp_attach, |
331 | | qp_detach, |
332 | | qp_makekey, |
333 | | qp_triename, |
334 | | }; |
335 | | |
336 | | static void |
337 | | qp_attach(void *uctx ISC_ATTR_UNUSED, void *pval, |
338 | 0 | uint32_t ival ISC_ATTR_UNUSED) { |
339 | 0 | qpcnode_t *data = pval; |
340 | 0 | qpcnode_ref(data); |
341 | 0 | } |
342 | | |
343 | | static void |
344 | | qp_detach(void *uctx ISC_ATTR_UNUSED, void *pval, |
345 | 0 | uint32_t ival ISC_ATTR_UNUSED) { |
346 | 0 | qpcnode_t *data = pval; |
347 | 0 | qpcnode_detach(&data); |
348 | 0 | } |
349 | | |
350 | | static size_t |
351 | | qp_makekey(dns_qpkey_t key, void *uctx ISC_ATTR_UNUSED, void *pval, |
352 | 0 | uint32_t ival ISC_ATTR_UNUSED) { |
353 | 0 | qpcnode_t *data = pval; |
354 | 0 | return dns_qpkey_fromname(key, &data->name, data->nspace); |
355 | 0 | } |
356 | | |
357 | | static void |
358 | 0 | qp_triename(void *uctx ISC_ATTR_UNUSED, char *buf, size_t size) { |
359 | 0 | snprintf(buf, size, "qpdb-lite"); |
360 | 0 | } |
361 | | |
362 | | static void |
363 | | rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG); |
364 | | static isc_result_t |
365 | | rdatasetiter_first(dns_rdatasetiter_t *iterator DNS__DB_FLARG); |
366 | | static isc_result_t |
367 | | rdatasetiter_next(dns_rdatasetiter_t *iterator DNS__DB_FLARG); |
368 | | static void |
369 | | rdatasetiter_current(dns_rdatasetiter_t *iterator, |
370 | | dns_rdataset_t *rdataset DNS__DB_FLARG); |
371 | | |
372 | | static dns_rdatasetitermethods_t rdatasetiter_methods = { |
373 | | rdatasetiter_destroy, rdatasetiter_first, rdatasetiter_next, |
374 | | rdatasetiter_current |
375 | | }; |
376 | | |
377 | | typedef struct qpc_rditer { |
378 | | dns_rdatasetiter_t common; |
379 | | dns_slabtop_t *current; |
380 | | } qpc_rditer_t; |
381 | | |
382 | | static void |
383 | | dbiterator_destroy(dns_dbiterator_t **iteratorp DNS__DB_FLARG); |
384 | | static isc_result_t |
385 | | dbiterator_first(dns_dbiterator_t *iterator DNS__DB_FLARG); |
386 | | static isc_result_t |
387 | | dbiterator_last(dns_dbiterator_t *iterator DNS__DB_FLARG); |
388 | | static isc_result_t |
389 | | dbiterator_seek(dns_dbiterator_t *iterator, |
390 | | const dns_name_t *name DNS__DB_FLARG); |
391 | | static isc_result_t |
392 | | dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG); |
393 | | static isc_result_t |
394 | | dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG); |
395 | | static isc_result_t |
396 | | dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep, |
397 | | dns_name_t *name DNS__DB_FLARG); |
398 | | static isc_result_t |
399 | | dbiterator_pause(dns_dbiterator_t *iterator); |
400 | | static isc_result_t |
401 | | dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name); |
402 | | |
403 | | static dns_dbiteratormethods_t dbiterator_methods = { |
404 | | dbiterator_destroy, dbiterator_first, dbiterator_last, |
405 | | dbiterator_seek, dbiterator_prev, dbiterator_next, |
406 | | dbiterator_current, dbiterator_pause, dbiterator_origin |
407 | | }; |
408 | | |
409 | | /* |
410 | | * In the cache, NSEC3 records are currently stored in the NORMAL |
411 | | * namespace. If we ever implement synth-from-dnssec using NSEC3 records, |
412 | | * they'll need be moved into the NSEC3 namespace for efficiency, and |
413 | | * the iterator implementation will need to be more complex, as in |
414 | | * qpzone. |
415 | | */ |
416 | | typedef struct qpc_dbit { |
417 | | dns_dbiterator_t common; |
418 | | bool paused; |
419 | | isc_rwlocktype_t tree_locked; |
420 | | isc_result_t result; |
421 | | dns_fixedname_t fixed; |
422 | | dns_name_t *name; |
423 | | dns_qpiter_t iter; |
424 | | qpcnode_t *node; |
425 | | } qpc_dbit_t; |
426 | | |
427 | | static void |
428 | | qpcache__destroy(qpcache_t *qpdb); |
429 | | |
430 | | static dns_dbmethods_t qpdb_cachemethods; |
431 | | |
432 | | static void |
433 | | cleanup_deadnodes_cb(void *arg); |
434 | | |
435 | | /* |
436 | | * Locking |
437 | | * |
438 | | * If a routine is going to lock more than one lock in this module, then |
439 | | * the locking must be done in the following order: |
440 | | * |
441 | | * Tree Lock |
442 | | * |
443 | | * Node Lock (Only one from the set may be locked at one time by |
444 | | * any caller) |
445 | | * |
446 | | * Database Lock |
447 | | * |
448 | | * Failure to follow this hierarchy can result in deadlock. |
449 | | */ |
450 | | |
451 | | /* |
452 | | * Cache-eviction routines. |
453 | | */ |
454 | | |
455 | | static size_t |
456 | | expireheader(dns_slabheader_t *header, isc_rwlocktype_t *nlocktypep, |
457 | | isc_rwlocktype_t *tlocktypep, dns_expire_t reason DNS__DB_FLARG); |
458 | | |
459 | | static size_t |
460 | 0 | rdataset_size(dns_slabheader_t *header) { |
461 | 0 | if (EXISTS(header)) { |
462 | 0 | return dns_rdataslab_size(header); |
463 | 0 | } |
464 | | |
465 | 0 | return sizeof(*header); |
466 | 0 | } |
467 | | |
468 | | static dns_slabheader_t * |
469 | 0 | first_header(dns_slabtop_t *top) { |
470 | 0 | dns_slabheader_t *header = NULL; |
471 | 0 | cds_list_for_each_entry(header, &top->headers, headers_link) { |
472 | 0 | return header; |
473 | 0 | } |
474 | 0 | return NULL; |
475 | 0 | } |
476 | | |
477 | | static dns_slabheader_t * |
478 | 0 | next_header(dns_slabheader_t *header) { |
479 | 0 | return cds_list_entry((header)->headers_link.next, dns_slabheader_t, |
480 | 0 | headers_link); |
481 | 0 | } |
482 | | |
483 | | static dns_slabheader_t * |
484 | 0 | first_existing_header(dns_slabtop_t *top) { |
485 | 0 | dns_slabheader_t *header = first_header(top); |
486 | 0 | if (EXISTS(header)) { |
487 | 0 | return header; |
488 | 0 | } |
489 | 0 | return NULL; |
490 | 0 | } |
491 | | |
492 | | static void |
493 | | expire_lru_headers(qpcache_t *qpdb, uint32_t idx, size_t requested, |
494 | | isc_rwlocktype_t *nlocktypep, |
495 | 0 | isc_rwlocktype_t *tlocktypep DNS__DB_FLARG) { |
496 | 0 | size_t expired = 0; |
497 | |
|
498 | 0 | do { |
499 | 0 | dns_slabtop_t *top = ISC_SIEVE_NEXT(qpdb->buckets[idx].sieve, |
500 | 0 | visited, link); |
501 | 0 | if (top == NULL) { |
502 | 0 | return; |
503 | 0 | } |
504 | | |
505 | 0 | dns_slabtop_t *related = top->related; |
506 | |
|
507 | 0 | if (ISC_SIEVE_LINKED(top, link)) { |
508 | 0 | ISC_SIEVE_UNLINK(qpdb->buckets[idx].sieve, top, link); |
509 | 0 | } |
510 | |
|
511 | 0 | dns_slabheader_t *header = first_header(top); |
512 | 0 | expired += expireheader(header, nlocktypep, tlocktypep, |
513 | 0 | dns_expire_lru DNS__DB_FLARG_PASS); |
514 | |
|
515 | 0 | if (related != NULL) { |
516 | 0 | header = first_header(related); |
517 | |
|
518 | 0 | expired += |
519 | 0 | expireheader(header, nlocktypep, tlocktypep, |
520 | 0 | dns_expire_lru DNS__DB_FLARG_PASS); |
521 | 0 | } |
522 | |
|
523 | 0 | } while (expired < requested); |
524 | 0 | } |
525 | | |
526 | | static void |
527 | | qpcache_miss(qpcache_t *qpdb, dns_slabheader_t *newheader, |
528 | | isc_rwlocktype_t *nlocktypep, |
529 | 0 | isc_rwlocktype_t *tlocktypep DNS__DB_FLARG) { |
530 | 0 | uint32_t idx = HEADERNODE(newheader)->locknum; |
531 | |
|
532 | 0 | isc_heap_insert(qpdb->buckets[idx].heap, newheader); |
533 | 0 | newheader->heap = qpdb->buckets[idx].heap; |
534 | |
|
535 | 0 | if (isc_mem_isovermem(qpdb->common.mctx)) { |
536 | | /* |
537 | | * Maximum estimated size of the data being added: The size |
538 | | * of the rdataset, plus a new QP database node and nodename, |
539 | | * and a possible additional NSEC node and nodename. Also add |
540 | | * a 12k margin for a possible QP-trie chunk allocation. |
541 | | * (It's okay to overestimate, we want to get cache memory |
542 | | * down quickly.) |
543 | | */ |
544 | |
|
545 | 0 | size_t purgesize = |
546 | 0 | 2 * (sizeof(qpcnode_t) + |
547 | 0 | dns_name_size(&HEADERNODE(newheader)->name)) + |
548 | 0 | rdataset_size(newheader) + QP_SAFETY_MARGIN; |
549 | |
|
550 | 0 | expire_lru_headers(qpdb, idx, purgesize, nlocktypep, |
551 | 0 | tlocktypep DNS__DB_FLARG_PASS); |
552 | 0 | } |
553 | |
|
554 | 0 | ISC_SIEVE_INSERT(qpdb->buckets[idx].sieve, newheader->top, link); |
555 | 0 | } |
556 | | |
557 | | static void |
558 | 0 | qpcache_hit(qpcache_t *qpdb ISC_ATTR_UNUSED, dns_slabheader_t *header) { |
559 | | /* |
560 | | * On cache hit, we only mark the header as seen. |
561 | | */ |
562 | 0 | ISC_SIEVE_MARK(header->top, visited); |
563 | 0 | } |
564 | | |
565 | | /* |
566 | | * DB Routines |
567 | | */ |
568 | | |
569 | | static void |
570 | 0 | clean_cache_headers(dns_slabtop_t *top) { |
571 | 0 | dns_slabheader_t *parent = first_header(top); |
572 | 0 | if (parent == NULL) { |
573 | 0 | return; |
574 | 0 | } |
575 | | |
576 | 0 | dns_slabheader_t *header = next_header(parent), *header_next = NULL; |
577 | 0 | cds_list_for_each_entry_safe_from(header, header_next, &top->headers, |
578 | 0 | headers_link) { |
579 | 0 | cds_list_del(&header->headers_link); |
580 | 0 | dns_slabheader_destroy(&header); |
581 | 0 | } |
582 | 0 | } |
583 | | |
584 | | static void |
585 | 0 | clean_cache_node(qpcache_t *qpdb, qpcnode_t *node) { |
586 | | /* |
587 | | * Caller must be holding the node lock. |
588 | | */ |
589 | |
|
590 | 0 | DNS_SLABTOP_FOREACH(top, node->data) { |
591 | 0 | clean_cache_headers(top); |
592 | | |
593 | | /* |
594 | | * If current top header is nonexistent, ancient, or stale |
595 | | * and we are not keeping stale, we can clean it up too. |
596 | | */ |
597 | 0 | dns_slabheader_t *header = first_header(top); |
598 | 0 | if (header == NULL) { |
599 | 0 | continue; |
600 | 0 | } |
601 | | |
602 | 0 | if (!EXISTS(header) || ANCIENT(header) || |
603 | 0 | (STALE(header) && !KEEPSTALE(qpdb))) |
604 | 0 | { |
605 | 0 | cds_list_del(&header->headers_link); |
606 | 0 | dns_slabheader_destroy(&header); |
607 | 0 | } |
608 | | |
609 | | /* |
610 | | * If current slabtop is empty, we can clean it up. |
611 | | */ |
612 | 0 | if (header == NULL) { |
613 | 0 | if (top->related != NULL) { |
614 | 0 | top->related->related = NULL; |
615 | 0 | top->related = NULL; |
616 | 0 | } |
617 | |
|
618 | 0 | cds_list_del(&top->types_link); |
619 | |
|
620 | 0 | if (ISC_SIEVE_LINKED(top, link)) { |
621 | 0 | ISC_SIEVE_UNLINK( |
622 | 0 | qpdb->buckets[node->locknum].sieve, top, |
623 | 0 | link); |
624 | 0 | } |
625 | 0 | dns_slabtop_destroy(((dns_db_t *)qpdb)->mctx, &top); |
626 | 0 | } |
627 | 0 | } |
628 | |
|
629 | 0 | node->dirty = false; |
630 | 0 | } |
631 | | |
632 | | /* |
633 | | * tree_lock(write) must be held. |
634 | | */ |
635 | | static void |
636 | 0 | delete_node(qpcache_t *qpdb, qpcnode_t *node) { |
637 | 0 | isc_result_t result = ISC_R_UNEXPECTED; |
638 | |
|
639 | 0 | if (isc_log_wouldlog(ISC_LOG_DEBUG(DNS_QPCACHE_LOG_STATS_LEVEL))) { |
640 | 0 | char printname[DNS_NAME_FORMATSIZE]; |
641 | 0 | dns_name_format(&node->name, printname, sizeof(printname)); |
642 | 0 | isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, |
643 | 0 | ISC_LOG_DEBUG(DNS_QPCACHE_LOG_STATS_LEVEL), |
644 | 0 | "delete_node(): %p %s (bucket %d)", node, |
645 | 0 | printname, node->locknum); |
646 | 0 | } |
647 | |
|
648 | 0 | switch (node->nspace) { |
649 | 0 | case DNS_DBNAMESPACE_NORMAL: |
650 | 0 | if (node->havensec) { |
651 | | /* |
652 | | * Delete the corresponding node from the auxiliary NSEC |
653 | | * tree before deleting from the main tree. |
654 | | */ |
655 | 0 | result = dns_qp_deletename(qpdb->tree, &node->name, |
656 | 0 | DNS_DBNAMESPACE_NSEC, NULL, |
657 | 0 | NULL); |
658 | 0 | if (result != ISC_R_SUCCESS) { |
659 | 0 | isc_log_write(DNS_LOGCATEGORY_DATABASE, |
660 | 0 | DNS_LOGMODULE_CACHE, |
661 | 0 | ISC_LOG_WARNING, |
662 | 0 | "delete_node(): " |
663 | 0 | "dns_qp_deletename: %s", |
664 | 0 | isc_result_totext(result)); |
665 | 0 | } |
666 | 0 | } |
667 | 0 | result = dns_qp_deletename(qpdb->tree, &node->name, |
668 | 0 | node->nspace, NULL, NULL); |
669 | 0 | break; |
670 | 0 | case DNS_DBNAMESPACE_NSEC: |
671 | 0 | result = dns_qp_deletename(qpdb->tree, &node->name, |
672 | 0 | node->nspace, NULL, NULL); |
673 | 0 | break; |
674 | 0 | } |
675 | 0 | if (result != ISC_R_SUCCESS) { |
676 | 0 | isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, |
677 | 0 | ISC_LOG_WARNING, |
678 | 0 | "delete_node(): " |
679 | 0 | "dns_qp_deletename: %s", |
680 | 0 | isc_result_totext(result)); |
681 | 0 | } |
682 | 0 | } |
683 | | |
684 | | /* |
685 | | * The caller must specify its currect node and tree lock status. |
686 | | * It's okay for neither lock to be held if there are existing external |
687 | | * references to the node, but if this is the first external reference, |
688 | | * then the caller must be holding at least one lock. |
689 | | * |
690 | | * If incrementing erefs from zero, we also increment the node use counter |
691 | | * in the qpcache object. |
692 | | * |
693 | | * This function is called from qpcnode_acquire(), so that internal |
694 | | * and external references are acquired at the same time, and from |
695 | | * qpcnode_release() when we only need to increase the internal references. |
696 | | */ |
697 | | static void |
698 | | qpcnode_erefs_increment(qpcache_t *qpdb, qpcnode_t *node, |
699 | | isc_rwlocktype_t nlocktype, |
700 | 0 | isc_rwlocktype_t tlocktype DNS__DB_FLARG) { |
701 | 0 | uint_fast32_t refs = isc_refcount_increment0(&node->erefs); |
702 | |
|
703 | | #if DNS_DB_NODETRACE |
704 | | fprintf(stderr, "incr:node:%s:%s:%u:%p->erefs = %" PRIuFAST32 "\n", |
705 | | func, file, line, node, refs + 1); |
706 | | #endif |
707 | |
|
708 | 0 | if (refs > 0) { |
709 | 0 | return; |
710 | 0 | } |
711 | | |
712 | | /* |
713 | | * this is the first external reference to the node. |
714 | | * |
715 | | * we need to hold the node or tree lock to avoid |
716 | | * incrementing the reference count while also deleting |
717 | | * the node. delete_node() is always protected by both |
718 | | * tree and node locks being write-locked. |
719 | | */ |
720 | 0 | INSIST(nlocktype != isc_rwlocktype_none || |
721 | 0 | tlocktype != isc_rwlocktype_none); |
722 | |
|
723 | 0 | qpcache_ref(qpdb); |
724 | 0 | } |
725 | | |
726 | | static void |
727 | | qpcnode_acquire(qpcache_t *qpdb, qpcnode_t *node, isc_rwlocktype_t nlocktype, |
728 | 0 | isc_rwlocktype_t tlocktype DNS__DB_FLARG) { |
729 | 0 | qpcnode_ref(node); |
730 | 0 | qpcnode_erefs_increment(qpdb, node, nlocktype, |
731 | 0 | tlocktype DNS__DB_FLARG_PASS); |
732 | 0 | } |
733 | | |
734 | | /* |
735 | | * Decrement the external references to a node. If the counter |
736 | | * goes to zero, decrement the node use counter in the qpcache object |
737 | | * as well, and return true. Otherwise return false. |
738 | | */ |
739 | | static bool |
740 | 0 | qpcnode_erefs_decrement(qpcache_t *qpdb, qpcnode_t *node DNS__DB_FLARG) { |
741 | 0 | uint_fast32_t refs = isc_refcount_decrement(&node->erefs); |
742 | |
|
743 | | #if DNS_DB_NODETRACE |
744 | | fprintf(stderr, "decr:node:%s:%s:%u:%p->erefs = %" PRIuFAST32 "\n", |
745 | | func, file, line, node, refs - 1); |
746 | | #endif |
747 | 0 | if (refs > 1) { |
748 | 0 | return false; |
749 | 0 | } |
750 | | |
751 | 0 | qpcache_unref(qpdb); |
752 | 0 | return true; |
753 | 0 | } |
754 | | |
755 | | /* |
756 | | * Caller must be holding a node lock, either read or write. |
757 | | * |
758 | | * Note that the lock must be held even when node references are |
759 | | * atomically modified; in that case the decrement operation itself does not |
760 | | * have to be protected, but we must avoid a race condition where multiple |
761 | | * threads are decreasing the reference to zero simultaneously and at least |
762 | | * one of them is going to free the node. |
763 | | * |
764 | | * This calls dec_erefs() to decrement the external node reference counter, |
765 | | * (and possibly the node use counter), cleans up and deletes the node |
766 | | * if necessary, then decrements the internal reference counter as well. |
767 | | */ |
768 | | static void |
769 | | qpcnode_release(qpcache_t *qpdb, qpcnode_t *node, isc_rwlocktype_t *nlocktypep, |
770 | 0 | isc_rwlocktype_t *tlocktypep DNS__DB_FLARG) { |
771 | 0 | REQUIRE(*nlocktypep != isc_rwlocktype_none); |
772 | |
|
773 | 0 | if (!qpcnode_erefs_decrement(qpdb, node DNS__DB_FLARG_PASS)) { |
774 | 0 | goto unref; |
775 | 0 | } |
776 | | |
777 | | /* Handle easy and typical case first. */ |
778 | 0 | if (!node->dirty && !cds_list_empty(node->data)) { |
779 | 0 | goto unref; |
780 | 0 | } |
781 | | |
782 | 0 | if (*nlocktypep == isc_rwlocktype_read) { |
783 | | /* |
784 | | * The external reference count went to zero and the node |
785 | | * is dirty or has no data, so we might want to delete it. |
786 | | * To do that, we'll need a write lock. If we don't already |
787 | | * have one, we have to make sure nobody else has |
788 | | * acquired a reference in the meantime, so we increment |
789 | | * erefs (but NOT references!), upgrade the node lock, |
790 | | * decrement erefs again, and see if it's still zero. |
791 | | * |
792 | | * We can't really assume anything about the result code of |
793 | | * erefs_increment. If another thread acquires reference it |
794 | | * will be larger than 0, if it doesn't it is going to be 0. |
795 | | */ |
796 | 0 | isc_rwlock_t *nlock = &qpdb->buckets[node->locknum].lock; |
797 | 0 | qpcnode_erefs_increment(qpdb, node, *nlocktypep, |
798 | 0 | *tlocktypep DNS__DB_FLARG_PASS); |
799 | 0 | NODE_FORCEUPGRADE(nlock, nlocktypep); |
800 | 0 | if (!qpcnode_erefs_decrement(qpdb, node DNS__DB_FLARG_PASS)) { |
801 | 0 | goto unref; |
802 | 0 | } |
803 | 0 | } |
804 | | |
805 | 0 | if (node->dirty) { |
806 | 0 | clean_cache_node(qpdb, node); |
807 | 0 | } |
808 | |
|
809 | 0 | if (!cds_list_empty(node->data)) { |
810 | 0 | goto unref; |
811 | 0 | } |
812 | | |
813 | 0 | if (*tlocktypep == isc_rwlocktype_write) { |
814 | | /* |
815 | | * We can delete the node if we have the tree write lock. |
816 | | */ |
817 | 0 | delete_node(qpdb, node); |
818 | 0 | } else { |
819 | | /* |
820 | | * If we don't have the tree lock, we will add this node to a |
821 | | * linked list of nodes in this locking bucket which we will |
822 | | * free later. |
823 | | */ |
824 | 0 | qpcnode_acquire(qpdb, node, *nlocktypep, |
825 | 0 | *tlocktypep DNS__DB_FLARG_PASS); |
826 | |
|
827 | 0 | isc_queue_node_init(&node->deadlink); |
828 | 0 | if (!isc_queue_enqueue_entry( |
829 | 0 | &qpdb->buckets[node->locknum].deadnodes, node, |
830 | 0 | deadlink)) |
831 | 0 | { |
832 | | /* Queue was empty, trigger new cleaning */ |
833 | 0 | isc_loop_t *loop = isc_loop_get(node->locknum); |
834 | |
|
835 | 0 | qpcache_ref(qpdb); |
836 | 0 | isc_async_run(loop, cleanup_deadnodes_cb, qpdb); |
837 | 0 | } |
838 | 0 | } |
839 | |
|
840 | 0 | unref: |
841 | 0 | qpcnode_unref(node); |
842 | 0 | } |
843 | | |
844 | | static void |
845 | | update_rrsetstats(dns_stats_t *stats, const dns_typepair_t typepair, |
846 | 0 | const uint_least16_t hattributes, const bool increment) { |
847 | 0 | dns_rdatastatstype_t statattributes = 0; |
848 | 0 | dns_rdatastatstype_t base = 0; |
849 | 0 | dns_rdatastatstype_t type; |
850 | 0 | dns_slabheader_t *header = &(dns_slabheader_t){ |
851 | 0 | .typepair = typepair, |
852 | 0 | .attributes = hattributes, |
853 | 0 | }; |
854 | |
|
855 | 0 | if (!EXISTS(header) || !STATCOUNT(header)) { |
856 | 0 | return; |
857 | 0 | } |
858 | | |
859 | 0 | if (NEGATIVE(header)) { |
860 | 0 | if (NXDOMAIN(header)) { |
861 | 0 | statattributes = DNS_RDATASTATSTYPE_ATTR_NXDOMAIN; |
862 | 0 | } else { |
863 | 0 | statattributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET; |
864 | 0 | base = DNS_TYPEPAIR_TYPE(header->typepair); |
865 | 0 | } |
866 | 0 | } else { |
867 | 0 | base = DNS_TYPEPAIR_TYPE(header->typepair); |
868 | 0 | } |
869 | |
|
870 | 0 | if (STALE(header)) { |
871 | 0 | statattributes |= DNS_RDATASTATSTYPE_ATTR_STALE; |
872 | 0 | } |
873 | 0 | if (ANCIENT(header)) { |
874 | 0 | statattributes |= DNS_RDATASTATSTYPE_ATTR_ANCIENT; |
875 | 0 | } |
876 | |
|
877 | 0 | type = DNS_RDATASTATSTYPE_VALUE(base, statattributes); |
878 | 0 | if (increment) { |
879 | 0 | dns_rdatasetstats_increment(stats, type); |
880 | 0 | } else { |
881 | 0 | dns_rdatasetstats_decrement(stats, type); |
882 | 0 | } |
883 | 0 | } |
884 | | |
885 | | static void |
886 | 0 | mark(dns_slabheader_t *header, uint_least16_t flag) { |
887 | 0 | uint_least16_t attributes = atomic_load_acquire(&header->attributes); |
888 | 0 | uint_least16_t newattributes = 0; |
889 | 0 | qpcache_t *qpdb = HEADERNODE(header)->qpdb; |
890 | | |
891 | | /* |
892 | | * If we are already ancient there is nothing to do. |
893 | | */ |
894 | 0 | do { |
895 | 0 | if ((attributes & flag) != 0) { |
896 | 0 | return; |
897 | 0 | } |
898 | 0 | newattributes = attributes | flag; |
899 | 0 | } while (!atomic_compare_exchange_weak_acq_rel( |
900 | 0 | &header->attributes, &attributes, newattributes)); |
901 | | |
902 | | /* |
903 | | * Decrement and increment the stats counter for the appropriate |
904 | | * RRtype. |
905 | | */ |
906 | 0 | update_rrsetstats(qpdb->rrsetstats, header->typepair, attributes, |
907 | 0 | false); |
908 | 0 | update_rrsetstats(qpdb->rrsetstats, header->typepair, newattributes, |
909 | 0 | true); |
910 | 0 | } |
911 | | |
912 | | static void |
913 | 0 | setttl(dns_slabheader_t *header, isc_stdtime_t newts) { |
914 | 0 | isc_stdtime_t oldts = header->expire; |
915 | |
|
916 | 0 | header->expire = newts; |
917 | |
|
918 | 0 | if (header->heap == NULL || header->heap_index == 0 || newts == oldts) { |
919 | 0 | return; |
920 | 0 | } |
921 | | |
922 | 0 | if (newts < oldts) { |
923 | 0 | isc_heap_increased(header->heap, header->heap_index); |
924 | 0 | } else { |
925 | 0 | isc_heap_decreased(header->heap, header->heap_index); |
926 | 0 | } |
927 | |
|
928 | 0 | if (newts == 0) { |
929 | 0 | isc_heap_delete(header->heap, header->heap_index); |
930 | 0 | } |
931 | 0 | } |
932 | | |
933 | | static void |
934 | 0 | mark_ancient(dns_slabheader_t *header) { |
935 | 0 | setttl(header, 0); |
936 | 0 | mark(header, DNS_SLABHEADERATTR_ANCIENT); |
937 | 0 | HEADERNODE(header)->dirty = 1; |
938 | 0 | } |
939 | | |
940 | | /* |
941 | | * Caller must hold the node (write) lock. |
942 | | */ |
943 | | static size_t |
944 | | expireheader(dns_slabheader_t *header, isc_rwlocktype_t *nlocktypep, |
945 | 0 | isc_rwlocktype_t *tlocktypep, dns_expire_t reason DNS__DB_FLARG) { |
946 | 0 | size_t expired = rdataset_size(header); |
947 | |
|
948 | 0 | mark_ancient(header); |
949 | |
|
950 | 0 | if (isc_refcount_current(&HEADERNODE(header)->erefs) == 0) { |
951 | 0 | qpcache_t *qpdb = HEADERNODE(header)->qpdb; |
952 | | |
953 | | /* |
954 | | * If no one else is using the node, we can clean it up now. |
955 | | * We first need to gain a new reference to the node to meet a |
956 | | * requirement of qpcnode_release(). |
957 | | */ |
958 | 0 | qpcnode_acquire(qpdb, HEADERNODE(header), *nlocktypep, |
959 | 0 | *tlocktypep DNS__DB_FLARG_PASS); |
960 | 0 | qpcnode_release(qpdb, HEADERNODE(header), nlocktypep, |
961 | 0 | tlocktypep DNS__DB_FLARG_PASS); |
962 | |
|
963 | 0 | if (qpdb->cachestats == NULL) { |
964 | 0 | return expired; |
965 | 0 | } |
966 | | |
967 | 0 | switch (reason) { |
968 | 0 | case dns_expire_ttl: |
969 | 0 | isc_stats_increment(qpdb->cachestats, |
970 | 0 | dns_cachestatscounter_deletettl); |
971 | 0 | break; |
972 | 0 | case dns_expire_lru: |
973 | 0 | isc_stats_increment(qpdb->cachestats, |
974 | 0 | dns_cachestatscounter_deletelru); |
975 | 0 | break; |
976 | 0 | default: |
977 | 0 | break; |
978 | 0 | } |
979 | 0 | } |
980 | | |
981 | 0 | return expired; |
982 | 0 | } |
983 | | |
984 | | static void |
985 | 0 | update_cachestats(qpcache_t *qpdb, isc_result_t result) { |
986 | 0 | if (qpdb->cachestats == NULL) { |
987 | 0 | return; |
988 | 0 | } |
989 | | |
990 | 0 | switch (result) { |
991 | 0 | case DNS_R_COVERINGNSEC: |
992 | 0 | isc_stats_increment(qpdb->cachestats, |
993 | 0 | dns_cachestatscounter_coveringnsec); |
994 | 0 | FALLTHROUGH; |
995 | 0 | case ISC_R_SUCCESS: |
996 | 0 | case DNS_R_CNAME: |
997 | 0 | case DNS_R_DNAME: |
998 | 0 | case DNS_R_DELEGATION: |
999 | 0 | case DNS_R_NCACHENXDOMAIN: |
1000 | 0 | case DNS_R_NCACHENXRRSET: |
1001 | 0 | isc_stats_increment(qpdb->cachestats, |
1002 | 0 | dns_cachestatscounter_hits); |
1003 | 0 | break; |
1004 | 0 | default: |
1005 | 0 | isc_stats_increment(qpdb->cachestats, |
1006 | 0 | dns_cachestatscounter_misses); |
1007 | 0 | } |
1008 | 0 | } |
1009 | | |
1010 | | static void |
1011 | | bindrdataset(qpcache_t *qpdb, qpcnode_t *node, dns_slabheader_t *header, |
1012 | | isc_stdtime_t now, isc_rwlocktype_t nlocktype, |
1013 | | isc_rwlocktype_t tlocktype, |
1014 | 0 | dns_rdataset_t *rdataset DNS__DB_FLARG) { |
1015 | 0 | bool stale = STALE(header); |
1016 | 0 | bool ancient = ANCIENT(header); |
1017 | | |
1018 | | /* |
1019 | | * Caller must be holding the node reader lock. |
1020 | | * XXXJT: technically, we need a writer lock, since we'll increment |
1021 | | * the header count below. However, since the actual counter value |
1022 | | * doesn't matter, we prioritize performance here. (We may want to |
1023 | | * use atomic increment when available). |
1024 | | */ |
1025 | |
|
1026 | 0 | if (rdataset == NULL) { |
1027 | 0 | return; |
1028 | 0 | } |
1029 | | |
1030 | 0 | qpcnode_acquire(qpdb, node, nlocktype, tlocktype DNS__DB_FLARG_PASS); |
1031 | |
|
1032 | 0 | INSIST(rdataset->methods == NULL); /* We must be disassociated. */ |
1033 | | |
1034 | | /* |
1035 | | * Mark header stale or ancient if the RRset is no longer active. |
1036 | | */ |
1037 | 0 | if (!ACTIVE(header, now)) { |
1038 | 0 | dns_ttl_t stale_ttl = header->expire + STALE_TTL(header, qpdb); |
1039 | | /* |
1040 | | * If this data is in the stale window keep it and if |
1041 | | * DNS_DBFIND_STALEOK is not set we tell the caller to |
1042 | | * skip this record. We skip the records with ZEROTTL |
1043 | | * (these records should not be cached anyway). |
1044 | | */ |
1045 | |
|
1046 | 0 | if (!ZEROTTL(header) && KEEPSTALE(qpdb) && stale_ttl > now) { |
1047 | 0 | stale = true; |
1048 | 0 | } else { |
1049 | | /* |
1050 | | * We are not keeping stale, or it is outside the |
1051 | | * stale window. Mark ancient, i.e. ready for cleanup. |
1052 | | */ |
1053 | 0 | ancient = true; |
1054 | 0 | } |
1055 | 0 | } |
1056 | |
|
1057 | 0 | rdataset->methods = &dns_rdataslab_rdatasetmethods; |
1058 | 0 | rdataset->rdclass = qpdb->common.rdclass; |
1059 | 0 | if (NEGATIVE(header)) { |
1060 | 0 | rdataset->type = dns_rdatatype_none; |
1061 | 0 | rdataset->covers = DNS_TYPEPAIR_TYPE(header->typepair); |
1062 | 0 | INSIST(DNS_TYPEPAIR_COVERS(header->typepair) == |
1063 | 0 | dns_rdatatype_none); |
1064 | 0 | } else { |
1065 | 0 | rdataset->type = DNS_TYPEPAIR_TYPE(header->typepair); |
1066 | 0 | rdataset->covers = DNS_TYPEPAIR_COVERS(header->typepair); |
1067 | 0 | } |
1068 | 0 | rdataset->ttl = !ZEROTTL(header) ? header->expire - now : 0; |
1069 | 0 | rdataset->trust = atomic_load(&header->trust); |
1070 | 0 | rdataset->resign = 0; |
1071 | |
|
1072 | 0 | if (NEGATIVE(header)) { |
1073 | 0 | rdataset->attributes.negative = true; |
1074 | 0 | } |
1075 | 0 | if (NXDOMAIN(header)) { |
1076 | 0 | rdataset->attributes.nxdomain = true; |
1077 | 0 | } |
1078 | 0 | if (OPTOUT(header)) { |
1079 | 0 | rdataset->attributes.optout = true; |
1080 | 0 | } |
1081 | 0 | if (PREFETCH(header)) { |
1082 | 0 | rdataset->attributes.prefetch = true; |
1083 | 0 | } |
1084 | |
|
1085 | 0 | if (stale && !ancient) { |
1086 | 0 | dns_ttl_t stale_ttl = header->expire + STALE_TTL(header, qpdb); |
1087 | 0 | if (stale_ttl > now) { |
1088 | 0 | rdataset->ttl = stale_ttl - now; |
1089 | 0 | } else { |
1090 | 0 | rdataset->ttl = 0; |
1091 | 0 | } |
1092 | 0 | if (STALE_WINDOW(header)) { |
1093 | 0 | rdataset->attributes.stale_window = true; |
1094 | 0 | } |
1095 | 0 | rdataset->attributes.stale = true; |
1096 | 0 | rdataset->expire = header->expire; |
1097 | 0 | } else if (!ACTIVE(header, now)) { |
1098 | 0 | rdataset->attributes.ancient = true; |
1099 | 0 | rdataset->ttl = 0; |
1100 | 0 | } |
1101 | |
|
1102 | 0 | rdataset->slab.db = (dns_db_t *)qpdb; |
1103 | 0 | rdataset->slab.node = (dns_dbnode_t *)node; |
1104 | 0 | rdataset->slab.raw = header->raw; |
1105 | 0 | rdataset->slab.iter_pos = NULL; |
1106 | 0 | rdataset->slab.iter_count = 0; |
1107 | | |
1108 | | /* |
1109 | | * Add noqname proof. |
1110 | | */ |
1111 | 0 | rdataset->slab.noqname = header->noqname; |
1112 | 0 | if (header->noqname != NULL) { |
1113 | 0 | rdataset->attributes.noqname = true; |
1114 | 0 | } |
1115 | 0 | rdataset->slab.closest = header->closest; |
1116 | 0 | if (header->closest != NULL) { |
1117 | 0 | rdataset->attributes.closest = true; |
1118 | 0 | } |
1119 | 0 | } |
1120 | | |
1121 | | static void |
1122 | | bindrdatasets(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *found, |
1123 | | dns_slabheader_t *foundsig, isc_stdtime_t now, |
1124 | | isc_rwlocktype_t nlocktype, isc_rwlocktype_t tlocktype, |
1125 | | dns_rdataset_t *rdataset, |
1126 | 0 | dns_rdataset_t *sigrdataset DNS__DB_FLARG) { |
1127 | 0 | bindrdataset(qpdb, qpnode, found, now, nlocktype, tlocktype, |
1128 | 0 | rdataset DNS__DB_FLARG_PASS); |
1129 | 0 | qpcache_hit(qpdb, found); |
1130 | 0 | if (!NEGATIVE(found) && foundsig != NULL) { |
1131 | 0 | bindrdataset(qpdb, qpnode, foundsig, now, nlocktype, tlocktype, |
1132 | 0 | sigrdataset DNS__DB_FLARG_PASS); |
1133 | 0 | qpcache_hit(qpdb, foundsig); |
1134 | 0 | } |
1135 | 0 | } |
1136 | | |
1137 | | static isc_result_t |
1138 | | setup_delegation(qpc_search_t *search, dns_dbnode_t **nodep, |
1139 | | dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, |
1140 | 0 | isc_rwlocktype_t tlocktype DNS__DB_FLARG) { |
1141 | 0 | dns_typepair_t typepair; |
1142 | 0 | qpcnode_t *node = NULL; |
1143 | |
|
1144 | 0 | REQUIRE(search != NULL); |
1145 | 0 | REQUIRE(search->zonecut != NULL); |
1146 | 0 | REQUIRE(search->zonecut_header != NULL); |
1147 | | |
1148 | | /* |
1149 | | * The caller MUST NOT be holding any node locks. |
1150 | | */ |
1151 | |
|
1152 | 0 | node = search->zonecut; |
1153 | 0 | typepair = search->zonecut_header->typepair; |
1154 | |
|
1155 | 0 | if (nodep != NULL) { |
1156 | | /* |
1157 | | * Note that we don't have to increment the node's reference |
1158 | | * count here because we're going to use the reference we |
1159 | | * already have in the search block. |
1160 | | */ |
1161 | 0 | *nodep = (dns_dbnode_t *)node; |
1162 | 0 | search->need_cleanup = false; |
1163 | 0 | } |
1164 | 0 | if (rdataset != NULL) { |
1165 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
1166 | 0 | isc_rwlock_t *nlock = |
1167 | 0 | &search->qpdb->buckets[node->locknum].lock; |
1168 | 0 | NODE_RDLOCK(nlock, &nlocktype); |
1169 | 0 | bindrdatasets(search->qpdb, node, search->zonecut_header, |
1170 | 0 | search->zonecut_sigheader, search->now, nlocktype, |
1171 | 0 | tlocktype, rdataset, |
1172 | 0 | sigrdataset DNS__DB_FLARG_PASS); |
1173 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
1174 | 0 | } |
1175 | | |
1176 | 0 | if (typepair == DNS_TYPEPAIR_VALUE(dns_rdatatype_dname, 0)) { |
1177 | 0 | return DNS_R_DNAME; |
1178 | 0 | } |
1179 | 0 | return DNS_R_DELEGATION; |
1180 | 0 | } |
1181 | | |
1182 | | static bool |
1183 | 0 | check_stale_header(dns_slabheader_t *header, qpc_search_t *search) { |
1184 | 0 | if (ACTIVE(header, search->now)) { |
1185 | 0 | return false; |
1186 | 0 | } |
1187 | | |
1188 | 0 | isc_stdtime_t stale = header->expire + STALE_TTL(header, search->qpdb); |
1189 | | /* |
1190 | | * If this data is in the stale window keep it and if |
1191 | | * DNS_DBFIND_STALEOK is not set we tell the caller to |
1192 | | * skip this record. We skip the records with ZEROTTL |
1193 | | * (these records should not be cached anyway). |
1194 | | */ |
1195 | |
|
1196 | 0 | DNS_SLABHEADER_CLRATTR(header, DNS_SLABHEADERATTR_STALE_WINDOW); |
1197 | 0 | if (!ZEROTTL(header) && KEEPSTALE(search->qpdb) && stale > search->now) |
1198 | 0 | { |
1199 | 0 | mark(header, DNS_SLABHEADERATTR_STALE); |
1200 | | /* |
1201 | | * If DNS_DBFIND_STALESTART is set then it means we |
1202 | | * failed to resolve the name during recursion, in |
1203 | | * this case we mark the time in which the refresh |
1204 | | * failed. |
1205 | | */ |
1206 | 0 | if ((search->options & DNS_DBFIND_STALESTART) != 0) { |
1207 | 0 | atomic_store_release(&header->last_refresh_fail_ts, |
1208 | 0 | search->now); |
1209 | 0 | } else if ((search->options & DNS_DBFIND_STALEENABLED) != 0 && |
1210 | 0 | search->now < |
1211 | 0 | (atomic_load_acquire( |
1212 | 0 | &header->last_refresh_fail_ts) + |
1213 | 0 | search->qpdb->serve_stale_refresh)) |
1214 | 0 | { |
1215 | | /* |
1216 | | * If we are within interval between last |
1217 | | * refresh failure time + 'stale-refresh-time', |
1218 | | * then don't skip this stale entry but use it |
1219 | | * instead. |
1220 | | */ |
1221 | 0 | DNS_SLABHEADER_SETATTR(header, |
1222 | 0 | DNS_SLABHEADERATTR_STALE_WINDOW); |
1223 | 0 | return false; |
1224 | 0 | } else if ((search->options & DNS_DBFIND_STALETIMEOUT) != 0) { |
1225 | | /* |
1226 | | * We want stale RRset due to timeout, so we |
1227 | | * don't skip it. |
1228 | | */ |
1229 | 0 | return false; |
1230 | 0 | } |
1231 | 0 | return (search->options & DNS_DBFIND_STALEOK) == 0; |
1232 | 0 | } |
1233 | | |
1234 | 0 | return true; |
1235 | 0 | } |
1236 | | |
1237 | | static bool |
1238 | 0 | check_header(dns_slabheader_t *header, qpc_search_t *search) { |
1239 | 0 | return header == NULL || check_stale_header(header, search) || |
1240 | 0 | !EXISTS(header) || ANCIENT(header); |
1241 | 0 | } |
1242 | | |
1243 | | /* |
1244 | | * Return true if we've found headers for both 'type' and RRSIG('type'), |
1245 | | * or (optionally, if 'negtype' is nonzero) if we've found a single |
1246 | | * negative header covering either 'negtype' or ANY. |
1247 | | */ |
1248 | | static bool |
1249 | | related_headers(dns_slabheader_t *header, dns_slabheader_t *sigheader, |
1250 | | dns_typepair_t typepair, dns_slabheader_t **foundp, |
1251 | 0 | dns_slabheader_t **foundsigp) { |
1252 | 0 | if (header != NULL) { |
1253 | 0 | REQUIRE(DNS_TYPEPAIR_TYPE(header->typepair) != |
1254 | 0 | dns_rdatatype_rrsig); |
1255 | 0 | REQUIRE(DNS_TYPEPAIR_COVERS(header->typepair) == |
1256 | 0 | dns_rdatatype_none); |
1257 | 0 | } |
1258 | 0 | if (sigheader != NULL) { |
1259 | 0 | REQUIRE(DNS_TYPEPAIR_TYPE(sigheader->typepair) == |
1260 | 0 | dns_rdatatype_rrsig); |
1261 | 0 | REQUIRE(DNS_TYPEPAIR_COVERS(sigheader->typepair) != |
1262 | 0 | dns_rdatatype_none || |
1263 | 0 | NEGATIVE(sigheader)); |
1264 | 0 | } |
1265 | | |
1266 | | /* |
1267 | | * Nothing exists if there's a NEGATIVE(dns_typepair_any). |
1268 | | */ |
1269 | 0 | if (header != NULL && header->typepair == dns_typepair_any) { |
1270 | 0 | INSIST(NEGATIVE(header)); |
1271 | 0 | INSIST(sigheader == NULL); |
1272 | 0 | *foundp = header; |
1273 | 0 | *foundsigp = NULL; |
1274 | 0 | return true; |
1275 | 0 | } |
1276 | | |
1277 | | /* |
1278 | | * Use the sigheader if we are looking for RRSIG. |
1279 | | */ |
1280 | 0 | if (DNS_TYPEPAIR_TYPE(typepair) == dns_rdatatype_rrsig) { |
1281 | 0 | if (sigheader == NULL) { |
1282 | 0 | return false; |
1283 | 0 | } |
1284 | | |
1285 | 0 | REQUIRE(EXISTS(sigheader) && !ANCIENT(sigheader)); |
1286 | 0 | if (sigheader->typepair == typepair) { |
1287 | 0 | *foundp = sigheader; |
1288 | 0 | *foundsigp = NULL; |
1289 | 0 | return true; |
1290 | 0 | } |
1291 | 0 | return false; |
1292 | 0 | } else { |
1293 | 0 | if (header == NULL) { |
1294 | 0 | return false; |
1295 | 0 | } |
1296 | | |
1297 | 0 | REQUIRE(EXISTS(header) && !ANCIENT(header)); |
1298 | 0 | REQUIRE(!NEGATIVE(header) || sigheader == NULL); |
1299 | |
|
1300 | 0 | if (header->typepair == typepair) { |
1301 | 0 | *foundp = header; |
1302 | 0 | *foundsigp = sigheader; |
1303 | 0 | return true; |
1304 | 0 | } |
1305 | 0 | } |
1306 | | |
1307 | 0 | return false; |
1308 | 0 | } |
1309 | | |
1310 | | static void |
1311 | | find_headers(qpcnode_t *node, qpc_search_t *search, dns_rdatatype_t type, |
1312 | 0 | dns_slabheader_t **foundp, dns_slabheader_t **foundsigp) { |
1313 | 0 | DNS_SLABTOP_FOREACH(top, node->data) { |
1314 | 0 | dns_slabheader_t *header = NULL, *sigheader = NULL; |
1315 | 0 | dns_slabtop_t *related = top->related; |
1316 | |
|
1317 | 0 | if (top->typepair == dns_typepair_any) { |
1318 | 0 | INSIST(top->related == NULL); |
1319 | 0 | header = first_header(top); |
1320 | 0 | INSIST(NEGATIVE(header)); |
1321 | 0 | if (check_header(header, search)) { |
1322 | | /* |
1323 | | * NEGATIVE(ANY), but it is no longer valid. |
1324 | | */ |
1325 | 0 | header = NULL; |
1326 | 0 | continue; |
1327 | 0 | } |
1328 | 0 | *foundp = NULL; |
1329 | 0 | *foundsigp = NULL; |
1330 | 0 | return; |
1331 | 0 | } |
1332 | | |
1333 | 0 | if (top->typepair == DNS_TYPEPAIR(type)) { |
1334 | 0 | header = first_header(top); |
1335 | 0 | if (related != NULL) { |
1336 | 0 | sigheader = first_header(related); |
1337 | 0 | } |
1338 | 0 | } else if (top->typepair == DNS_SIGTYPEPAIR(type)) { |
1339 | 0 | sigheader = first_header(top); |
1340 | 0 | if (related != NULL) { |
1341 | 0 | header = first_header(related); |
1342 | 0 | } |
1343 | 0 | } else { |
1344 | | /* Not our type; continue with next slabtop */ |
1345 | 0 | continue; |
1346 | 0 | } |
1347 | | |
1348 | 0 | if (check_header(header, search)) { |
1349 | 0 | header = NULL; |
1350 | 0 | } |
1351 | 0 | if (check_header(sigheader, search)) { |
1352 | 0 | sigheader = NULL; |
1353 | 0 | } |
1354 | | |
1355 | | /* |
1356 | | * This function only sets positive headers. |
1357 | | */ |
1358 | 0 | if (header != NULL && !NEGATIVE(header)) { |
1359 | 0 | *foundp = header; |
1360 | 0 | *foundsigp = sigheader; |
1361 | 0 | } |
1362 | |
|
1363 | 0 | return; |
1364 | 0 | } |
1365 | 0 | } |
1366 | | |
1367 | | static isc_result_t |
1368 | 0 | check_zonecut(qpcnode_t *node, void *arg DNS__DB_FLARG) { |
1369 | 0 | qpc_search_t *search = arg; |
1370 | 0 | dns_slabheader_t *found = NULL, *foundsig = NULL; |
1371 | 0 | isc_result_t result; |
1372 | 0 | isc_rwlock_t *nlock = NULL; |
1373 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
1374 | |
|
1375 | 0 | REQUIRE(search->zonecut == NULL); |
1376 | |
|
1377 | 0 | nlock = &search->qpdb->buckets[node->locknum].lock; |
1378 | 0 | NODE_RDLOCK(nlock, &nlocktype); |
1379 | | |
1380 | | /* |
1381 | | * Look for a DNAME or RRSIG DNAME rdataset. |
1382 | | */ |
1383 | 0 | find_headers(node, search, dns_rdatatype_dname, &found, &foundsig); |
1384 | |
|
1385 | 0 | if (found != NULL && (!DNS_TRUST_PENDING(atomic_load(&found->trust)) || |
1386 | 0 | (search->options & DNS_DBFIND_PENDINGOK) != 0)) |
1387 | 0 | { |
1388 | | /* |
1389 | | * We increment the reference count on node to ensure that |
1390 | | * search->zonecut_header will still be valid later. |
1391 | | */ |
1392 | 0 | qpcnode_acquire(search->qpdb, node, nlocktype, |
1393 | 0 | isc_rwlocktype_none DNS__DB_FLARG_PASS); |
1394 | 0 | search->zonecut = node; |
1395 | 0 | search->zonecut_header = found; |
1396 | 0 | search->zonecut_sigheader = foundsig; |
1397 | 0 | search->need_cleanup = true; |
1398 | 0 | result = DNS_R_PARTIALMATCH; |
1399 | 0 | } else { |
1400 | 0 | result = DNS_R_CONTINUE; |
1401 | 0 | } |
1402 | |
|
1403 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
1404 | |
|
1405 | 0 | return result; |
1406 | 0 | } |
1407 | | |
1408 | | static isc_result_t |
1409 | | find_deepest_zonecut(qpc_search_t *search, qpcnode_t *node, |
1410 | | dns_dbnode_t **nodep, dns_name_t *foundname, |
1411 | | dns_rdataset_t *rdataset, |
1412 | 0 | dns_rdataset_t *sigrdataset DNS__DB_FLARG) { |
1413 | 0 | isc_result_t result = ISC_R_NOTFOUND; |
1414 | 0 | qpcache_t *qpdb = NULL; |
1415 | | |
1416 | | /* |
1417 | | * Caller must be holding the tree lock. |
1418 | | */ |
1419 | |
|
1420 | 0 | qpdb = search->qpdb; |
1421 | |
|
1422 | 0 | for (int i = dns_qpchain_length(&search->chain) - 1; i >= 0; i--) { |
1423 | 0 | dns_slabheader_t *found = NULL, *foundsig = NULL; |
1424 | 0 | isc_rwlock_t *nlock = NULL; |
1425 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
1426 | |
|
1427 | 0 | dns_qpchain_node(&search->chain, i, NULL, (void **)&node, NULL); |
1428 | 0 | nlock = &qpdb->buckets[node->locknum].lock; |
1429 | |
|
1430 | 0 | NODE_RDLOCK(nlock, &nlocktype); |
1431 | | |
1432 | | /* |
1433 | | * Look for NS and RRSIG NS rdatasets. |
1434 | | */ |
1435 | 0 | find_headers(node, search, dns_rdatatype_ns, &found, &foundsig); |
1436 | |
|
1437 | 0 | if (found != NULL) { |
1438 | | /* |
1439 | | * If we have to set foundname, we do it before |
1440 | | * anything else. |
1441 | | */ |
1442 | 0 | if (foundname != NULL) { |
1443 | 0 | dns_name_copy(&node->name, foundname); |
1444 | 0 | } |
1445 | 0 | result = DNS_R_DELEGATION; |
1446 | 0 | if (nodep != NULL) { |
1447 | 0 | qpcnode_acquire( |
1448 | 0 | search->qpdb, node, nlocktype, |
1449 | 0 | isc_rwlocktype_none DNS__DB_FLARG_PASS); |
1450 | 0 | *nodep = (dns_dbnode_t *)node; |
1451 | 0 | } |
1452 | 0 | bindrdatasets(search->qpdb, node, found, foundsig, |
1453 | 0 | search->now, nlocktype, |
1454 | 0 | isc_rwlocktype_none, rdataset, |
1455 | 0 | sigrdataset DNS__DB_FLARG_PASS); |
1456 | 0 | } |
1457 | |
|
1458 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
1459 | |
|
1460 | 0 | if (found != NULL) { |
1461 | 0 | break; |
1462 | 0 | } |
1463 | 0 | } |
1464 | | |
1465 | 0 | return result; |
1466 | 0 | } |
1467 | | |
1468 | | /* |
1469 | | * Look for a potentially covering NSEC in the cache where `name` |
1470 | | * is known not to exist. This uses the auxiliary NSEC tree to find |
1471 | | * the potential NSEC owner. If found, we update 'foundname', 'nodep', |
1472 | | * 'rdataset' and 'sigrdataset', and return DNS_R_COVERINGNSEC. |
1473 | | * Otherwise, return ISC_R_NOTFOUND. |
1474 | | */ |
1475 | | static isc_result_t |
1476 | | find_coveringnsec(qpc_search_t *search, const dns_name_t *name, |
1477 | | dns_dbnode_t **nodep, dns_name_t *foundname, |
1478 | | dns_rdataset_t *rdataset, |
1479 | 0 | dns_rdataset_t *sigrdataset DNS__DB_FLARG) { |
1480 | 0 | dns_fixedname_t fpredecessor, fixed; |
1481 | 0 | dns_name_t *predecessor = NULL, *fname = NULL; |
1482 | 0 | qpcnode_t *node = NULL; |
1483 | 0 | dns_qpiter_t iter; |
1484 | 0 | isc_result_t result; |
1485 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
1486 | 0 | isc_rwlock_t *nlock = NULL; |
1487 | 0 | dns_slabheader_t *found = NULL, *foundsig = NULL; |
1488 | | |
1489 | | /* |
1490 | | * Look for the node in the auxiliary NSEC namespace. |
1491 | | */ |
1492 | 0 | result = dns_qp_lookup(search->qpdb->tree, name, DNS_DBNAMESPACE_NSEC, |
1493 | 0 | NULL, &iter, NULL, (void **)&node, NULL); |
1494 | | /* |
1495 | | * When DNS_R_PARTIALMATCH or ISC_R_NOTFOUND is returned from |
1496 | | * dns_qp_lookup there is potentially a covering NSEC present |
1497 | | * in the cache so we need to search for it. Otherwise we are |
1498 | | * done here. |
1499 | | */ |
1500 | 0 | if (result != DNS_R_PARTIALMATCH && result != ISC_R_NOTFOUND) { |
1501 | 0 | return ISC_R_NOTFOUND; |
1502 | 0 | } |
1503 | | |
1504 | 0 | fname = dns_fixedname_initname(&fixed); |
1505 | 0 | predecessor = dns_fixedname_initname(&fpredecessor); |
1506 | | |
1507 | | /* |
1508 | | * Extract predecessor from iterator. |
1509 | | */ |
1510 | 0 | result = dns_qpiter_current(&iter, predecessor, NULL, NULL); |
1511 | 0 | if (result != ISC_R_SUCCESS) { |
1512 | 0 | return ISC_R_NOTFOUND; |
1513 | 0 | } |
1514 | | |
1515 | | /* |
1516 | | * Lookup the predecessor in the normal namespace. |
1517 | | */ |
1518 | 0 | node = NULL; |
1519 | 0 | result = dns_qp_getname(search->qpdb->tree, predecessor, |
1520 | 0 | DNS_DBNAMESPACE_NORMAL, (void **)&node, NULL); |
1521 | 0 | if (result != ISC_R_SUCCESS) { |
1522 | 0 | return result; |
1523 | 0 | } |
1524 | 0 | dns_name_copy(&node->name, fname); |
1525 | |
|
1526 | 0 | nlock = &search->qpdb->buckets[node->locknum].lock; |
1527 | 0 | NODE_RDLOCK(nlock, &nlocktype); |
1528 | |
|
1529 | 0 | find_headers(node, search, dns_rdatatype_nsec, &found, &foundsig); |
1530 | |
|
1531 | 0 | if (found != NULL) { |
1532 | 0 | if (nodep != NULL) { |
1533 | 0 | qpcnode_acquire(search->qpdb, node, nlocktype, |
1534 | 0 | isc_rwlocktype_none DNS__DB_FLARG_PASS); |
1535 | 0 | *nodep = (dns_dbnode_t *)node; |
1536 | 0 | } |
1537 | 0 | bindrdatasets(search->qpdb, node, found, foundsig, search->now, |
1538 | 0 | nlocktype, isc_rwlocktype_none, rdataset, |
1539 | 0 | sigrdataset DNS__DB_FLARG_PASS); |
1540 | 0 | dns_name_copy(fname, foundname); |
1541 | |
|
1542 | 0 | result = DNS_R_COVERINGNSEC; |
1543 | 0 | } else { |
1544 | 0 | result = ISC_R_NOTFOUND; |
1545 | 0 | } |
1546 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
1547 | 0 | return result; |
1548 | 0 | } |
1549 | | |
1550 | | static inline bool |
1551 | 0 | missing_answer(dns_slabheader_t *found, unsigned int options) { |
1552 | 0 | if (found == NULL) { |
1553 | 0 | return true; |
1554 | 0 | } |
1555 | | |
1556 | 0 | dns_trust_t trust = atomic_load(&found->trust); |
1557 | 0 | return (DNS_TRUST_ADDITIONAL(trust) && |
1558 | 0 | (options & DNS_DBFIND_ADDITIONALOK) == 0) || |
1559 | 0 | (DNS_TRUST_GLUE(trust) && (options & DNS_DBFIND_GLUEOK) == 0) || |
1560 | 0 | (DNS_TRUST_PENDING(trust) && |
1561 | 0 | (options & DNS_DBFIND_PENDINGOK) == 0); |
1562 | 0 | } |
1563 | | |
1564 | | static void |
1565 | | qpc_search_init(qpc_search_t *search, qpcache_t *db, unsigned int options, |
1566 | 0 | isc_stdtime_t now) { |
1567 | | /* |
1568 | | * qpc_search_t contains two structures with large buffers (dns_qpiter_t |
1569 | | * and dns_qpchain_t). Those two structures will be initialized later by |
1570 | | * dns_qp_lookup anyway. |
1571 | | * To avoid the overhead of zero initialization, we avoid designated |
1572 | | * initializers and initialize all "small" fields manually. |
1573 | | */ |
1574 | 0 | search->qpdb = (qpcache_t *)db; |
1575 | 0 | search->options = options; |
1576 | | /* |
1577 | | * qpch->in - Init by dns_qp_lookup |
1578 | | * qpiter - Init by dns_qp_lookup |
1579 | | */ |
1580 | 0 | search->need_cleanup = false; |
1581 | 0 | search->now = now ? now : isc_stdtime_now(); |
1582 | 0 | search->zonecut = NULL; |
1583 | 0 | search->zonecut_header = NULL; |
1584 | 0 | search->zonecut_sigheader = NULL; |
1585 | 0 | } |
1586 | | |
1587 | | static isc_result_t |
1588 | | qpcache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, |
1589 | | dns_rdatatype_t type, unsigned int options, isc_stdtime_t __now, |
1590 | | dns_dbnode_t **nodep, dns_name_t *foundname, |
1591 | | dns_clientinfomethods_t *methods ISC_ATTR_UNUSED, |
1592 | | dns_clientinfo_t *clientinfo ISC_ATTR_UNUSED, |
1593 | | dns_rdataset_t *rdataset, |
1594 | 0 | dns_rdataset_t *sigrdataset DNS__DB_FLARG) { |
1595 | 0 | qpcnode_t *node = NULL; |
1596 | 0 | isc_result_t result; |
1597 | 0 | bool cname_ok = true; |
1598 | 0 | bool found_noqname = false; |
1599 | 0 | bool all_negative = true; |
1600 | 0 | bool empty_node; |
1601 | 0 | isc_rwlock_t *nlock = NULL; |
1602 | 0 | isc_rwlocktype_t tlocktype = isc_rwlocktype_none; |
1603 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
1604 | 0 | dns_slabheader_t *found = NULL, *foundsig = NULL; |
1605 | 0 | dns_slabheader_t *nsheader = NULL, *nssig = NULL; |
1606 | 0 | dns_slabheader_t *nsecheader = NULL, *nsecsig = NULL; |
1607 | 0 | dns_typepair_t typepair; |
1608 | |
|
1609 | 0 | if (type == dns_rdatatype_none) { |
1610 | | /* We can't search negative cache directly */ |
1611 | 0 | return ISC_R_NOTFOUND; |
1612 | 0 | } |
1613 | | |
1614 | 0 | qpc_search_t search; |
1615 | 0 | qpc_search_init(&search, (qpcache_t *)db, options, __now); |
1616 | |
|
1617 | 0 | REQUIRE(VALID_QPDB((qpcache_t *)db)); |
1618 | 0 | REQUIRE(version == NULL); |
1619 | |
|
1620 | 0 | TREE_RDLOCK(&search.qpdb->tree_lock, &tlocktype); |
1621 | | |
1622 | | /* |
1623 | | * Search down from the root of the tree. |
1624 | | */ |
1625 | 0 | result = dns_qp_lookup(search.qpdb->tree, name, DNS_DBNAMESPACE_NORMAL, |
1626 | 0 | NULL, NULL, &search.chain, (void **)&node, NULL); |
1627 | 0 | if (result != ISC_R_NOTFOUND && foundname != NULL) { |
1628 | 0 | dns_name_copy(&node->name, foundname); |
1629 | 0 | } |
1630 | | |
1631 | | /* |
1632 | | * Check the QP chain to see if there's a node above us with a |
1633 | | * active DNAME or NS rdatasets. |
1634 | | * |
1635 | | * We're only interested in nodes above QNAME, so if the result |
1636 | | * was success, then we skip the last item in the chain. |
1637 | | */ |
1638 | 0 | unsigned int len = dns_qpchain_length(&search.chain); |
1639 | 0 | if (result == ISC_R_SUCCESS) { |
1640 | 0 | len--; |
1641 | 0 | } |
1642 | |
|
1643 | 0 | for (unsigned int i = 0; i < len; i++) { |
1644 | 0 | isc_result_t zcresult; |
1645 | 0 | qpcnode_t *encloser = NULL; |
1646 | |
|
1647 | 0 | dns_qpchain_node(&search.chain, i, NULL, (void **)&encloser, |
1648 | 0 | NULL); |
1649 | |
|
1650 | 0 | zcresult = check_zonecut(encloser, |
1651 | 0 | (void *)&search DNS__DB_FLARG_PASS); |
1652 | 0 | if (zcresult != DNS_R_CONTINUE) { |
1653 | 0 | result = DNS_R_PARTIALMATCH; |
1654 | 0 | search.chain.len = i - 1; |
1655 | 0 | node = encloser; |
1656 | 0 | if (foundname != NULL) { |
1657 | 0 | dns_name_copy(&node->name, foundname); |
1658 | 0 | } |
1659 | 0 | break; |
1660 | 0 | } |
1661 | 0 | } |
1662 | |
|
1663 | 0 | if (result == DNS_R_PARTIALMATCH) { |
1664 | | /* |
1665 | | * If we discovered a covering DNAME skip looking for a covering |
1666 | | * NSEC. |
1667 | | */ |
1668 | 0 | if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0 && |
1669 | 0 | (search.zonecut_header == NULL || |
1670 | 0 | search.zonecut_header->typepair != dns_rdatatype_dname)) |
1671 | 0 | { |
1672 | 0 | result = find_coveringnsec( |
1673 | 0 | &search, name, nodep, foundname, rdataset, |
1674 | 0 | sigrdataset DNS__DB_FLARG_PASS); |
1675 | 0 | if (result == DNS_R_COVERINGNSEC) { |
1676 | 0 | goto tree_exit; |
1677 | 0 | } |
1678 | 0 | } |
1679 | 0 | if (search.zonecut != NULL) { |
1680 | 0 | result = setup_delegation(&search, nodep, rdataset, |
1681 | 0 | sigrdataset, |
1682 | 0 | tlocktype DNS__DB_FLARG_PASS); |
1683 | 0 | goto tree_exit; |
1684 | 0 | } else { |
1685 | 0 | find_ns: |
1686 | 0 | result = find_deepest_zonecut( |
1687 | 0 | &search, node, nodep, foundname, rdataset, |
1688 | 0 | sigrdataset DNS__DB_FLARG_PASS); |
1689 | 0 | goto tree_exit; |
1690 | 0 | } |
1691 | 0 | } else if (result != ISC_R_SUCCESS) { |
1692 | 0 | goto tree_exit; |
1693 | 0 | } |
1694 | | |
1695 | | /* |
1696 | | * Certain DNSSEC types are not subject to CNAME matching |
1697 | | * (RFC4035, section 2.5 and RFC3007). |
1698 | | */ |
1699 | 0 | if (type == dns_rdatatype_key || type == dns_rdatatype_nsec || |
1700 | 0 | type == dns_rdatatype_rrsig) |
1701 | 0 | { |
1702 | 0 | cname_ok = false; |
1703 | 0 | } |
1704 | | |
1705 | | /* |
1706 | | * We now go looking for rdata... |
1707 | | */ |
1708 | |
|
1709 | 0 | nlock = &search.qpdb->buckets[node->locknum].lock; |
1710 | 0 | NODE_RDLOCK(nlock, &nlocktype); |
1711 | | |
1712 | | /* |
1713 | | * These pointers need to be reset here in case we did |
1714 | | * 'goto find_ns' from somewhere below. |
1715 | | */ |
1716 | 0 | found = NULL; |
1717 | 0 | foundsig = NULL; |
1718 | 0 | typepair = DNS_TYPEPAIR(type); |
1719 | 0 | nsheader = NULL; |
1720 | 0 | nsecheader = NULL; |
1721 | 0 | nssig = NULL; |
1722 | 0 | nsecsig = NULL; |
1723 | 0 | empty_node = true; |
1724 | |
|
1725 | 0 | DNS_SLABTOP_FOREACH(top, node->data) { |
1726 | 0 | dns_slabheader_t *header = NULL, *sigheader = NULL; |
1727 | 0 | if (DNS_TYPEPAIR_TYPE(top->typepair) == dns_rdatatype_rrsig) { |
1728 | 0 | sigheader = first_header(top); |
1729 | 0 | if (top->related != NULL) { |
1730 | 0 | header = first_header(top->related); |
1731 | 0 | } |
1732 | 0 | } else { |
1733 | 0 | header = first_header(top); |
1734 | 0 | if (top->related != NULL) { |
1735 | 0 | sigheader = first_header(top->related); |
1736 | 0 | } |
1737 | 0 | } |
1738 | |
|
1739 | 0 | if (check_header(header, &search)) { |
1740 | 0 | header = NULL; |
1741 | 0 | } |
1742 | |
|
1743 | 0 | if (check_header(sigheader, &search)) { |
1744 | 0 | sigheader = NULL; |
1745 | 0 | } |
1746 | |
|
1747 | 0 | if (header == NULL && sigheader == NULL) { |
1748 | 0 | continue; |
1749 | 0 | } |
1750 | | |
1751 | | /* |
1752 | | * We now know that there is at least one active |
1753 | | * non-stale rdataset at this node. |
1754 | | */ |
1755 | 0 | empty_node = false; |
1756 | |
|
1757 | 0 | if (header != NULL && header->noqname != NULL && |
1758 | 0 | atomic_load(&header->trust) == dns_trust_secure) |
1759 | 0 | { |
1760 | 0 | found_noqname = true; |
1761 | 0 | } |
1762 | |
|
1763 | 0 | if (header != NULL && !NEGATIVE(header)) { |
1764 | 0 | all_negative = false; |
1765 | 0 | } |
1766 | |
|
1767 | 0 | if (sigheader != NULL && !NEGATIVE(sigheader)) { |
1768 | 0 | all_negative = false; |
1769 | 0 | } |
1770 | |
|
1771 | 0 | if (related_headers(header, sigheader, typepair, &found, |
1772 | 0 | &foundsig)) |
1773 | 0 | { |
1774 | | /* |
1775 | | * We can't exit early until we have an answer with |
1776 | | * sufficient trust level - see missing_answer() |
1777 | | * for details - because we might need NS or NSEC |
1778 | | * records. |
1779 | | */ |
1780 | 0 | if (missing_answer(found, options)) { |
1781 | 0 | continue; |
1782 | 0 | } |
1783 | | |
1784 | | /* We found something, continue with next header */ |
1785 | 0 | break; |
1786 | 0 | } |
1787 | | |
1788 | 0 | if (header == NULL || NEGATIVE(header)) { |
1789 | | /* |
1790 | | * We are not interested in the negative headers for the |
1791 | | * auxiliary types, only for the main type we are |
1792 | | * looking for. |
1793 | | */ |
1794 | 0 | continue; |
1795 | 0 | } |
1796 | | |
1797 | 0 | switch (top->typepair) { |
1798 | 0 | case dns_rdatatype_cname: |
1799 | 0 | case DNS_SIGTYPEPAIR(dns_rdatatype_cname): |
1800 | 0 | if (cname_ok) { |
1801 | 0 | found = header; |
1802 | 0 | foundsig = sigheader; |
1803 | 0 | } |
1804 | 0 | break; |
1805 | | |
1806 | 0 | case dns_rdatatype_ns: |
1807 | 0 | case DNS_SIGTYPEPAIR(dns_rdatatype_ns): |
1808 | 0 | nsheader = header; |
1809 | 0 | nssig = sigheader; |
1810 | 0 | break; |
1811 | | |
1812 | 0 | case dns_rdatatype_nsec: |
1813 | 0 | case DNS_SIGTYPEPAIR(dns_rdatatype_nsec): |
1814 | 0 | nsecheader = header; |
1815 | 0 | nsecsig = sigheader; |
1816 | 0 | break; |
1817 | | |
1818 | 0 | default: |
1819 | 0 | if (typepair == dns_typepair_any) { |
1820 | | /* QTYPE==ANY, so any anwers will do */ |
1821 | 0 | found = header; |
1822 | 0 | break; |
1823 | 0 | } |
1824 | 0 | } |
1825 | | |
1826 | 0 | if (!missing_answer(found, options)) { |
1827 | 0 | break; |
1828 | 0 | } |
1829 | 0 | } |
1830 | | |
1831 | 0 | if (empty_node) { |
1832 | | /* |
1833 | | * We have an exact match for the name, but there are no |
1834 | | * extant rdatasets. That means that this node doesn't |
1835 | | * meaningfully exist, and that we really have a partial match. |
1836 | | */ |
1837 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
1838 | 0 | if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0) { |
1839 | 0 | result = find_coveringnsec( |
1840 | 0 | &search, name, nodep, foundname, rdataset, |
1841 | 0 | sigrdataset DNS__DB_FLARG_PASS); |
1842 | 0 | if (result == DNS_R_COVERINGNSEC) { |
1843 | 0 | goto tree_exit; |
1844 | 0 | } |
1845 | 0 | } |
1846 | 0 | goto find_ns; |
1847 | 0 | } |
1848 | | |
1849 | | /* |
1850 | | * If we didn't find what we were looking for... |
1851 | | */ |
1852 | 0 | if (missing_answer(found, options)) { |
1853 | | /* |
1854 | | * Return covering NODATA NSEC record. |
1855 | | */ |
1856 | 0 | if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0 && |
1857 | 0 | nsecheader != NULL) |
1858 | 0 | { |
1859 | 0 | if (nodep != NULL) { |
1860 | 0 | qpcnode_acquire(search.qpdb, node, nlocktype, |
1861 | 0 | tlocktype DNS__DB_FLARG_PASS); |
1862 | 0 | *nodep = (dns_dbnode_t *)node; |
1863 | 0 | } |
1864 | 0 | bindrdatasets(search.qpdb, node, nsecheader, nsecsig, |
1865 | 0 | search.now, nlocktype, tlocktype, |
1866 | 0 | rdataset, sigrdataset DNS__DB_FLARG_PASS); |
1867 | 0 | result = DNS_R_COVERINGNSEC; |
1868 | 0 | goto node_exit; |
1869 | 0 | } |
1870 | | |
1871 | | /* |
1872 | | * This name was from a wild card. Look for a covering NSEC. |
1873 | | */ |
1874 | 0 | if (found == NULL && (found_noqname || all_negative) && |
1875 | 0 | (search.options & DNS_DBFIND_COVERINGNSEC) != 0) |
1876 | 0 | { |
1877 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
1878 | 0 | result = find_coveringnsec( |
1879 | 0 | &search, name, nodep, foundname, rdataset, |
1880 | 0 | sigrdataset DNS__DB_FLARG_PASS); |
1881 | 0 | if (result == DNS_R_COVERINGNSEC) { |
1882 | 0 | goto tree_exit; |
1883 | 0 | } |
1884 | 0 | goto find_ns; |
1885 | 0 | } |
1886 | | |
1887 | | /* |
1888 | | * If there is an NS rdataset at this node, then this is the |
1889 | | * deepest zone cut. |
1890 | | */ |
1891 | 0 | if (nsheader != NULL) { |
1892 | 0 | if (nodep != NULL) { |
1893 | 0 | qpcnode_acquire(search.qpdb, node, nlocktype, |
1894 | 0 | tlocktype DNS__DB_FLARG_PASS); |
1895 | 0 | *nodep = (dns_dbnode_t *)node; |
1896 | 0 | } |
1897 | 0 | bindrdatasets(search.qpdb, node, nsheader, nssig, |
1898 | 0 | search.now, nlocktype, tlocktype, |
1899 | 0 | rdataset, sigrdataset DNS__DB_FLARG_PASS); |
1900 | 0 | result = DNS_R_DELEGATION; |
1901 | 0 | goto node_exit; |
1902 | 0 | } |
1903 | | |
1904 | | /* |
1905 | | * Go find the deepest zone cut. |
1906 | | */ |
1907 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
1908 | 0 | goto find_ns; |
1909 | 0 | } |
1910 | | |
1911 | | /* |
1912 | | * We found what we were looking for, or we found a CNAME. |
1913 | | */ |
1914 | | |
1915 | 0 | if (nodep != NULL) { |
1916 | 0 | qpcnode_acquire(search.qpdb, node, nlocktype, |
1917 | 0 | tlocktype DNS__DB_FLARG_PASS); |
1918 | 0 | *nodep = (dns_dbnode_t *)node; |
1919 | 0 | } |
1920 | |
|
1921 | 0 | if (NEGATIVE(found)) { |
1922 | | /* |
1923 | | * We found a negative cache entry. |
1924 | | */ |
1925 | 0 | if (NXDOMAIN(found)) { |
1926 | 0 | result = DNS_R_NCACHENXDOMAIN; |
1927 | 0 | } else { |
1928 | 0 | result = DNS_R_NCACHENXRRSET; |
1929 | 0 | } |
1930 | 0 | } else if (typepair != found->typepair && |
1931 | 0 | typepair != dns_typepair_any && |
1932 | 0 | found->typepair == DNS_TYPEPAIR(dns_rdatatype_cname)) |
1933 | 0 | { |
1934 | | /* |
1935 | | * We weren't doing an ANY query and we found a CNAME instead |
1936 | | * of the type we were looking for, so we need to indicate |
1937 | | * that result to the caller. |
1938 | | */ |
1939 | 0 | result = DNS_R_CNAME; |
1940 | 0 | } else { |
1941 | | /* |
1942 | | * An ordinary successful query! |
1943 | | */ |
1944 | 0 | result = ISC_R_SUCCESS; |
1945 | 0 | } |
1946 | |
|
1947 | 0 | if (typepair != dns_typepair_any || result == DNS_R_NCACHENXDOMAIN || |
1948 | 0 | result == DNS_R_NCACHENXRRSET) |
1949 | 0 | { |
1950 | 0 | bindrdatasets(search.qpdb, node, found, foundsig, search.now, |
1951 | 0 | nlocktype, tlocktype, rdataset, |
1952 | 0 | sigrdataset DNS__DB_FLARG_PASS); |
1953 | 0 | } |
1954 | |
|
1955 | 0 | node_exit: |
1956 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
1957 | |
|
1958 | 0 | tree_exit: |
1959 | 0 | TREE_UNLOCK(&search.qpdb->tree_lock, &tlocktype); |
1960 | | |
1961 | | /* |
1962 | | * If we found a zonecut but aren't going to use it, we have to |
1963 | | * let go of it. |
1964 | | */ |
1965 | 0 | if (search.need_cleanup) { |
1966 | 0 | node = search.zonecut; |
1967 | 0 | INSIST(node != NULL); |
1968 | 0 | nlock = &search.qpdb->buckets[node->locknum].lock; |
1969 | |
|
1970 | 0 | NODE_RDLOCK(nlock, &nlocktype); |
1971 | 0 | qpcnode_release(search.qpdb, node, &nlocktype, |
1972 | 0 | &tlocktype DNS__DB_FLARG_PASS); |
1973 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
1974 | 0 | INSIST(tlocktype == isc_rwlocktype_none); |
1975 | 0 | } |
1976 | | |
1977 | 0 | update_cachestats(search.qpdb, result); |
1978 | 0 | return result; |
1979 | 0 | } |
1980 | | |
1981 | | static isc_result_t |
1982 | | seek_ns_headers(qpc_search_t *search, qpcnode_t *node, dns_dbnode_t **nodep, |
1983 | | dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, |
1984 | | dns_name_t *foundname, dns_name_t *dcname, |
1985 | 0 | isc_rwlocktype_t *tlocktype) { |
1986 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
1987 | 0 | isc_rwlock_t *nlock = &search->qpdb->buckets[node->locknum].lock; |
1988 | 0 | dns_slabheader_t *found = NULL, *foundsig = NULL; |
1989 | |
|
1990 | 0 | NODE_RDLOCK(nlock, &nlocktype); |
1991 | |
|
1992 | 0 | find_headers(node, search, dns_rdatatype_ns, &found, &foundsig); |
1993 | |
|
1994 | 0 | if (found == NULL) { |
1995 | 0 | isc_result_t result; |
1996 | | |
1997 | | /* |
1998 | | * No active NS records found. Call find_deepest_zonecut() |
1999 | | * to look for them in nodes above this one. |
2000 | | */ |
2001 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
2002 | 0 | result = find_deepest_zonecut(search, node, nodep, foundname, |
2003 | 0 | rdataset, |
2004 | 0 | sigrdataset DNS__DB_FLARG_PASS); |
2005 | 0 | if (dcname != NULL) { |
2006 | 0 | dns_name_copy(foundname, dcname); |
2007 | 0 | } |
2008 | 0 | return result; |
2009 | 0 | } |
2010 | | |
2011 | 0 | if (nodep != NULL) { |
2012 | 0 | qpcnode_acquire(search->qpdb, node, nlocktype, |
2013 | 0 | *tlocktype DNS__DB_FLARG_PASS); |
2014 | 0 | *nodep = (dns_dbnode_t *)node; |
2015 | 0 | } |
2016 | |
|
2017 | 0 | bindrdatasets(search->qpdb, node, found, foundsig, search->now, |
2018 | 0 | nlocktype, *tlocktype, rdataset, |
2019 | 0 | sigrdataset DNS__DB_FLARG_PASS); |
2020 | |
|
2021 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
2022 | |
|
2023 | 0 | return ISC_R_SUCCESS; |
2024 | 0 | } |
2025 | | |
2026 | | static isc_result_t |
2027 | | qpcache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options, |
2028 | | isc_stdtime_t __now, dns_dbnode_t **nodep, |
2029 | | dns_name_t *foundname, dns_name_t *dcname, |
2030 | | dns_rdataset_t *rdataset, |
2031 | 0 | dns_rdataset_t *sigrdataset DNS__DB_FLARG) { |
2032 | 0 | qpcnode_t *node = NULL; |
2033 | 0 | isc_result_t result; |
2034 | 0 | isc_rwlocktype_t tlocktype = isc_rwlocktype_none; |
2035 | 0 | qpc_search_t search = (qpc_search_t){ |
2036 | 0 | .qpdb = (qpcache_t *)db, |
2037 | 0 | .options = options, |
2038 | 0 | .now = __now ? __now : isc_stdtime_now(), |
2039 | 0 | }; |
2040 | 0 | unsigned int len = 0; |
2041 | |
|
2042 | 0 | REQUIRE(VALID_QPDB((qpcache_t *)db)); |
2043 | |
|
2044 | 0 | TREE_RDLOCK(&search.qpdb->tree_lock, &tlocktype); |
2045 | | |
2046 | | /* |
2047 | | * Search down from the root of the tree. |
2048 | | */ |
2049 | 0 | result = dns_qp_lookup(search.qpdb->tree, name, DNS_DBNAMESPACE_NORMAL, |
2050 | 0 | NULL, NULL, &search.chain, (void **)&node, NULL); |
2051 | |
|
2052 | 0 | switch (result) { |
2053 | 0 | case ISC_R_SUCCESS: |
2054 | 0 | if ((options & DNS_DBFIND_NOEXACT) == 0) { |
2055 | 0 | if (dcname != NULL) { |
2056 | 0 | dns_name_copy(&node->name, dcname); |
2057 | 0 | } |
2058 | 0 | dns_name_copy(&node->name, foundname); |
2059 | 0 | result = seek_ns_headers(&search, node, nodep, rdataset, |
2060 | 0 | sigrdataset, foundname, dcname, |
2061 | 0 | &tlocktype); |
2062 | 0 | break; |
2063 | 0 | } |
2064 | | |
2065 | 0 | len = dns_qpchain_length(&search.chain); |
2066 | 0 | if (len < 2) { |
2067 | 0 | result = ISC_R_NOTFOUND; |
2068 | 0 | break; |
2069 | 0 | } |
2070 | | |
2071 | 0 | FALLTHROUGH; |
2072 | 0 | case DNS_R_PARTIALMATCH: |
2073 | 0 | if (dcname != NULL) { |
2074 | 0 | dns_name_copy(&node->name, dcname); |
2075 | 0 | } |
2076 | |
|
2077 | 0 | if (result == ISC_R_SUCCESS) { |
2078 | | /* Fell through from the previous case */ |
2079 | 0 | INSIST(len >= 2); |
2080 | |
|
2081 | 0 | node = NULL; |
2082 | 0 | dns_qpchain_node(&search.chain, len - 2, NULL, |
2083 | 0 | (void **)&node, NULL); |
2084 | 0 | search.chain.len = len - 1; |
2085 | 0 | } |
2086 | |
|
2087 | 0 | result = find_deepest_zonecut(&search, node, nodep, foundname, |
2088 | 0 | rdataset, |
2089 | 0 | sigrdataset DNS__DB_FLARG_PASS); |
2090 | 0 | break; |
2091 | 0 | default: |
2092 | 0 | break; |
2093 | 0 | } |
2094 | | |
2095 | 0 | TREE_UNLOCK(&search.qpdb->tree_lock, &tlocktype); |
2096 | |
|
2097 | 0 | INSIST(!search.need_cleanup); |
2098 | |
|
2099 | 0 | if (result == DNS_R_DELEGATION) { |
2100 | 0 | result = ISC_R_SUCCESS; |
2101 | 0 | } |
2102 | |
|
2103 | 0 | return result; |
2104 | 0 | } |
2105 | | |
2106 | | static isc_result_t |
2107 | | qpcache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, |
2108 | | dns_rdatatype_t type, dns_rdatatype_t covers, |
2109 | | isc_stdtime_t __now, dns_rdataset_t *rdataset, |
2110 | 0 | dns_rdataset_t *sigrdataset DNS__DB_FLARG) { |
2111 | 0 | qpcache_t *qpdb = (qpcache_t *)db; |
2112 | 0 | qpcnode_t *qpnode = (qpcnode_t *)node; |
2113 | 0 | dns_slabheader_t *found = NULL, *foundsig = NULL; |
2114 | 0 | dns_typepair_t typepair, sigpair; |
2115 | 0 | isc_result_t result = ISC_R_SUCCESS; |
2116 | 0 | isc_rwlock_t *nlock = NULL; |
2117 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
2118 | 0 | qpc_search_t search = (qpc_search_t){ |
2119 | 0 | .qpdb = (qpcache_t *)db, |
2120 | 0 | .now = __now ? __now : isc_stdtime_now(), |
2121 | 0 | }; |
2122 | |
|
2123 | 0 | REQUIRE(VALID_QPDB(qpdb)); |
2124 | 0 | REQUIRE(version == NULL); |
2125 | 0 | REQUIRE(type != dns_rdatatype_any); |
2126 | |
|
2127 | 0 | if (type == dns_rdatatype_none) { |
2128 | | /* We can't search negative cache directly */ |
2129 | 0 | return ISC_R_NOTFOUND; |
2130 | 0 | } |
2131 | | |
2132 | 0 | nlock = &qpdb->buckets[qpnode->locknum].lock; |
2133 | 0 | NODE_RDLOCK(nlock, &nlocktype); |
2134 | |
|
2135 | 0 | typepair = DNS_TYPEPAIR_VALUE(type, covers); |
2136 | 0 | sigpair = (type != dns_rdatatype_rrsig) ? DNS_SIGTYPEPAIR(type) |
2137 | 0 | : dns_typepair_none; |
2138 | |
|
2139 | 0 | DNS_SLABTOP_FOREACH(top, qpnode->data) { |
2140 | 0 | dns_slabheader_t *header = NULL, *sigheader = NULL; |
2141 | |
|
2142 | 0 | if (top->typepair != typepair && top->typepair != sigpair && |
2143 | 0 | top->typepair != dns_typepair_any) |
2144 | 0 | { |
2145 | 0 | continue; |
2146 | 0 | } |
2147 | | |
2148 | 0 | if (DNS_TYPEPAIR_TYPE(top->typepair) == dns_rdatatype_rrsig) { |
2149 | 0 | sigheader = first_header(top); |
2150 | 0 | if (top->related != NULL) { |
2151 | 0 | header = first_header(top->related); |
2152 | 0 | } |
2153 | 0 | } else { |
2154 | 0 | header = first_header(top); |
2155 | 0 | if (top->related != NULL) { |
2156 | 0 | sigheader = first_header(top->related); |
2157 | 0 | } |
2158 | 0 | } |
2159 | |
|
2160 | 0 | if (check_header(header, &search)) { |
2161 | 0 | header = NULL; |
2162 | 0 | } |
2163 | |
|
2164 | 0 | if (check_header(sigheader, &search)) { |
2165 | 0 | sigheader = NULL; |
2166 | 0 | } |
2167 | |
|
2168 | 0 | (void)related_headers(header, sigheader, typepair, &found, |
2169 | 0 | &foundsig); |
2170 | 0 | break; |
2171 | 0 | } |
2172 | |
|
2173 | 0 | if (found != NULL) { |
2174 | 0 | bindrdatasets(qpdb, qpnode, found, foundsig, search.now, |
2175 | 0 | nlocktype, isc_rwlocktype_none, rdataset, |
2176 | 0 | sigrdataset DNS__DB_FLARG_PASS); |
2177 | 0 | } |
2178 | |
|
2179 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
2180 | |
|
2181 | 0 | if (found == NULL) { |
2182 | 0 | return ISC_R_NOTFOUND; |
2183 | 0 | } |
2184 | | |
2185 | 0 | if (NEGATIVE(found)) { |
2186 | | /* |
2187 | | * We found a negative cache entry. |
2188 | | */ |
2189 | 0 | if (NXDOMAIN(found)) { |
2190 | 0 | result = DNS_R_NCACHENXDOMAIN; |
2191 | 0 | } else { |
2192 | 0 | result = DNS_R_NCACHENXRRSET; |
2193 | 0 | } |
2194 | 0 | } |
2195 | |
|
2196 | 0 | update_cachestats(qpdb, result); |
2197 | |
|
2198 | 0 | return result; |
2199 | 0 | } |
2200 | | |
2201 | | static isc_result_t |
2202 | 0 | setcachestats(dns_db_t *db, isc_stats_t *stats) { |
2203 | 0 | qpcache_t *qpdb = (qpcache_t *)db; |
2204 | |
|
2205 | 0 | REQUIRE(VALID_QPDB(qpdb)); |
2206 | 0 | REQUIRE(stats != NULL); |
2207 | |
|
2208 | 0 | isc_stats_attach(stats, &qpdb->cachestats); |
2209 | 0 | return ISC_R_SUCCESS; |
2210 | 0 | } |
2211 | | |
2212 | | static dns_stats_t * |
2213 | 0 | getrrsetstats(dns_db_t *db) { |
2214 | 0 | qpcache_t *qpdb = (qpcache_t *)db; |
2215 | |
|
2216 | 0 | REQUIRE(VALID_QPDB(qpdb)); |
2217 | |
|
2218 | 0 | return qpdb->rrsetstats; |
2219 | 0 | } |
2220 | | |
2221 | | static isc_result_t |
2222 | 0 | setservestalettl(dns_db_t *db, dns_ttl_t ttl) { |
2223 | 0 | qpcache_t *qpdb = (qpcache_t *)db; |
2224 | |
|
2225 | 0 | REQUIRE(VALID_QPDB(qpdb)); |
2226 | | |
2227 | | /* currently no bounds checking. 0 means disable. */ |
2228 | 0 | qpdb->common.serve_stale_ttl = ttl; |
2229 | 0 | return ISC_R_SUCCESS; |
2230 | 0 | } |
2231 | | |
2232 | | static isc_result_t |
2233 | 0 | getservestalettl(dns_db_t *db, dns_ttl_t *ttl) { |
2234 | 0 | qpcache_t *qpdb = (qpcache_t *)db; |
2235 | |
|
2236 | 0 | REQUIRE(VALID_QPDB(qpdb)); |
2237 | |
|
2238 | 0 | *ttl = qpdb->common.serve_stale_ttl; |
2239 | 0 | return ISC_R_SUCCESS; |
2240 | 0 | } |
2241 | | |
2242 | | static isc_result_t |
2243 | 0 | setservestalerefresh(dns_db_t *db, uint32_t interval) { |
2244 | 0 | qpcache_t *qpdb = (qpcache_t *)db; |
2245 | |
|
2246 | 0 | REQUIRE(VALID_QPDB(qpdb)); |
2247 | | |
2248 | | /* currently no bounds checking. 0 means disable. */ |
2249 | 0 | qpdb->serve_stale_refresh = interval; |
2250 | 0 | return ISC_R_SUCCESS; |
2251 | 0 | } |
2252 | | |
2253 | | static isc_result_t |
2254 | 0 | getservestalerefresh(dns_db_t *db, uint32_t *interval) { |
2255 | 0 | qpcache_t *qpdb = (qpcache_t *)db; |
2256 | |
|
2257 | 0 | REQUIRE(VALID_QPDB(qpdb)); |
2258 | |
|
2259 | 0 | *interval = qpdb->serve_stale_refresh; |
2260 | 0 | return ISC_R_SUCCESS; |
2261 | 0 | } |
2262 | | |
2263 | | static void |
2264 | 0 | qpcnode_expiredata(dns_dbnode_t *node, void *data) { |
2265 | 0 | qpcnode_t *qpnode = (qpcnode_t *)node; |
2266 | 0 | qpcache_t *qpdb = (qpcache_t *)qpnode->qpdb; |
2267 | |
|
2268 | 0 | dns_slabtop_t *related = NULL; |
2269 | 0 | dns_slabheader_t *header = data; |
2270 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
2271 | 0 | isc_rwlocktype_t tlocktype = isc_rwlocktype_none; |
2272 | |
|
2273 | 0 | isc_rwlock_t *nlock = &qpdb->buckets[qpnode->locknum].lock; |
2274 | 0 | NODE_WRLOCK(nlock, &nlocktype); |
2275 | 0 | related = header->top->related; |
2276 | |
|
2277 | 0 | (void)expireheader(header, &nlocktype, &tlocktype, |
2278 | 0 | dns_expire_flush DNS__DB_FILELINE); |
2279 | 0 | if (related != NULL) { |
2280 | 0 | header = first_header(related); |
2281 | 0 | (void)expireheader(header, &nlocktype, &tlocktype, |
2282 | 0 | dns_expire_flush DNS__DB_FILELINE); |
2283 | 0 | } |
2284 | |
|
2285 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
2286 | 0 | INSIST(tlocktype == isc_rwlocktype_none); |
2287 | 0 | } |
2288 | | |
2289 | | /*% |
2290 | | * These functions allow the heap code to rank the priority of each |
2291 | | * element. It returns true if v1 happens "sooner" than v2. |
2292 | | */ |
2293 | | static bool |
2294 | 0 | ttl_sooner(void *v1, void *v2) { |
2295 | 0 | dns_slabheader_t *h1 = v1; |
2296 | 0 | dns_slabheader_t *h2 = v2; |
2297 | |
|
2298 | 0 | return h1->expire < h2->expire; |
2299 | 0 | } |
2300 | | |
2301 | | /*% |
2302 | | * This function sets the heap index into the header. |
2303 | | */ |
2304 | | static void |
2305 | 0 | set_index(void *what, unsigned int idx) { |
2306 | 0 | dns_slabheader_t *h = what; |
2307 | |
|
2308 | 0 | h->heap_index = idx; |
2309 | 0 | } |
2310 | | |
2311 | | static void |
2312 | 0 | qpcache__destroy(qpcache_t *qpdb) { |
2313 | 0 | unsigned int i; |
2314 | 0 | char buf[DNS_NAME_FORMATSIZE]; |
2315 | |
|
2316 | 0 | dns_qp_destroy(&qpdb->tree); |
2317 | |
|
2318 | 0 | if (dns_name_dynamic(&qpdb->common.origin)) { |
2319 | 0 | dns_name_format(&qpdb->common.origin, buf, sizeof(buf)); |
2320 | 0 | } else { |
2321 | 0 | strlcpy(buf, "<UNKNOWN>", sizeof(buf)); |
2322 | 0 | } |
2323 | 0 | isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, |
2324 | 0 | ISC_LOG_DEBUG(DNS_QPCACHE_LOG_STATS_LEVEL), "done %s(%s)", |
2325 | 0 | __func__, buf); |
2326 | |
|
2327 | 0 | if (dns_name_dynamic(&qpdb->common.origin)) { |
2328 | 0 | dns_name_free(&qpdb->common.origin, qpdb->common.mctx); |
2329 | 0 | } |
2330 | 0 | for (i = 0; i < qpdb->buckets_count; i++) { |
2331 | 0 | NODE_DESTROYLOCK(&qpdb->buckets[i].lock); |
2332 | |
|
2333 | 0 | INSIST(ISC_SIEVE_EMPTY(qpdb->buckets[i].sieve)); |
2334 | |
|
2335 | 0 | INSIST(isc_queue_empty(&qpdb->buckets[i].deadnodes)); |
2336 | 0 | isc_queue_destroy(&qpdb->buckets[i].deadnodes); |
2337 | |
|
2338 | 0 | isc_heap_destroy(&qpdb->buckets[i].heap); |
2339 | 0 | } |
2340 | |
|
2341 | 0 | dns_stats_detach(&qpdb->rrsetstats); |
2342 | |
|
2343 | 0 | if (qpdb->cachestats != NULL) { |
2344 | 0 | isc_stats_detach(&qpdb->cachestats); |
2345 | 0 | } |
2346 | |
|
2347 | 0 | TREE_DESTROYLOCK(&qpdb->tree_lock); |
2348 | 0 | isc_refcount_destroy(&qpdb->references); |
2349 | 0 | isc_refcount_destroy(&qpdb->common.references); |
2350 | |
|
2351 | 0 | isc_rwlock_destroy(&qpdb->lock); |
2352 | 0 | qpdb->common.magic = 0; |
2353 | 0 | qpdb->common.impmagic = 0; |
2354 | 0 | isc_mem_detach(&qpdb->hmctx); |
2355 | |
|
2356 | 0 | isc_mem_putanddetach(&qpdb->common.mctx, qpdb, |
2357 | 0 | sizeof(*qpdb) + qpdb->buckets_count * |
2358 | 0 | sizeof(qpdb->buckets[0])); |
2359 | 0 | } |
2360 | | |
2361 | | static void |
2362 | 0 | qpcache_destroy(dns_db_t *arg) { |
2363 | 0 | qpcache_t *qpdb = (qpcache_t *)arg; |
2364 | |
|
2365 | 0 | qpcache_detach(&qpdb); |
2366 | 0 | } |
2367 | | |
2368 | | /*% |
2369 | | * Clean up dead nodes. These are nodes which have no references, and |
2370 | | * have no data. They are dead but we could not or chose not to delete |
2371 | | * them when we deleted all the data at that node because we did not want |
2372 | | * to wait for the tree write lock. |
2373 | | */ |
2374 | | static void |
2375 | 0 | cleanup_deadnodes(qpcache_t *qpdb, uint16_t locknum) { |
2376 | 0 | isc_rwlocktype_t tlocktype = isc_rwlocktype_none; |
2377 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
2378 | 0 | isc_rwlock_t *nlock = &qpdb->buckets[locknum].lock; |
2379 | 0 | qpcnode_t *qpnode = NULL, *qpnext = NULL; |
2380 | 0 | isc_queue_t deadnodes; |
2381 | |
|
2382 | 0 | INSIST(locknum < qpdb->buckets_count); |
2383 | |
|
2384 | 0 | isc_queue_init(&deadnodes); |
2385 | |
|
2386 | 0 | TREE_WRLOCK(&qpdb->tree_lock, &tlocktype); |
2387 | 0 | NODE_WRLOCK(nlock, &nlocktype); |
2388 | |
|
2389 | 0 | isc_queue_splice(&deadnodes, &qpdb->buckets[locknum].deadnodes); |
2390 | 0 | isc_queue_for_each_entry_safe(&deadnodes, qpnode, qpnext, deadlink) { |
2391 | 0 | qpcnode_release(qpdb, qpnode, &nlocktype, |
2392 | 0 | &tlocktype DNS__DB_FILELINE); |
2393 | 0 | } |
2394 | |
|
2395 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
2396 | 0 | TREE_UNLOCK(&qpdb->tree_lock, &tlocktype); |
2397 | 0 | } |
2398 | | |
2399 | | static void |
2400 | 0 | cleanup_deadnodes_cb(void *arg) { |
2401 | 0 | qpcache_t *qpdb = arg; |
2402 | 0 | uint16_t locknum = isc_tid(); |
2403 | |
|
2404 | 0 | cleanup_deadnodes(qpdb, locknum); |
2405 | 0 | qpcache_unref(qpdb); |
2406 | 0 | } |
2407 | | /* |
2408 | | * This function is assumed to be called when a node is newly referenced |
2409 | | * and can be in the deadnode list. In that case the node will be references |
2410 | | * and cleanup_deadnodes() will remove it from the list when the cleaning |
2411 | | * happens. |
2412 | | * Note: while a new reference is gained in multiple places, there are only very |
2413 | | * few cases where the node can be in the deadnode list (only empty nodes can |
2414 | | * have been added to the list). |
2415 | | */ |
2416 | | static void |
2417 | | reactivate_node(qpcache_t *qpdb, qpcnode_t *node, |
2418 | 0 | isc_rwlocktype_t tlocktype ISC_ATTR_UNUSED DNS__DB_FLARG) { |
2419 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
2420 | 0 | isc_rwlock_t *nlock = &qpdb->buckets[node->locknum].lock; |
2421 | |
|
2422 | 0 | NODE_RDLOCK(nlock, &nlocktype); |
2423 | 0 | qpcnode_acquire(qpdb, node, nlocktype, tlocktype DNS__DB_FLARG_PASS); |
2424 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
2425 | 0 | } |
2426 | | |
2427 | | static qpcnode_t * |
2428 | 0 | new_qpcnode(qpcache_t *qpdb, const dns_name_t *name, dns_namespace_t nspace) { |
2429 | 0 | qpcnode_t *newdata = isc_mem_get(qpdb->common.mctx, sizeof(*newdata)); |
2430 | 0 | *newdata = (qpcnode_t){ |
2431 | 0 | .types_list = CDS_LIST_HEAD_INIT(newdata->types_list), |
2432 | 0 | .data = &newdata->types_list, |
2433 | 0 | .methods = &qpcnode_methods, |
2434 | 0 | .qpdb = qpdb, |
2435 | 0 | .name = DNS_NAME_INITEMPTY, |
2436 | 0 | .nspace = nspace, |
2437 | 0 | .references = ISC_REFCOUNT_INITIALIZER(1), |
2438 | 0 | .locknum = isc_random_uniform(qpdb->buckets_count), |
2439 | 0 | }; |
2440 | |
|
2441 | 0 | isc_mem_attach(qpdb->common.mctx, &newdata->mctx); |
2442 | 0 | dns_name_dup(name, newdata->mctx, &newdata->name); |
2443 | |
|
2444 | | #ifdef DNS_DB_NODETRACE |
2445 | | fprintf(stderr, "new_qpcnode:%s:%s:%d:%p->references = 1\n", __func__, |
2446 | | __FILE__, __LINE__ + 1, name); |
2447 | | #endif |
2448 | 0 | return newdata; |
2449 | 0 | } |
2450 | | |
2451 | | static isc_result_t |
2452 | | qpcache_findnode(dns_db_t *db, const dns_name_t *name, bool create, |
2453 | | dns_clientinfomethods_t *methods ISC_ATTR_UNUSED, |
2454 | | dns_clientinfo_t *clientinfo ISC_ATTR_UNUSED, |
2455 | 0 | dns_dbnode_t **nodep DNS__DB_FLARG) { |
2456 | 0 | qpcache_t *qpdb = (qpcache_t *)db; |
2457 | 0 | qpcnode_t *node = NULL; |
2458 | 0 | isc_result_t result; |
2459 | 0 | isc_rwlocktype_t tlocktype = isc_rwlocktype_none; |
2460 | 0 | dns_namespace_t nspace = DNS_DBNAMESPACE_NORMAL; |
2461 | |
|
2462 | 0 | TREE_RDLOCK(&qpdb->tree_lock, &tlocktype); |
2463 | 0 | result = dns_qp_getname(qpdb->tree, name, nspace, (void **)&node, NULL); |
2464 | 0 | if (result != ISC_R_SUCCESS) { |
2465 | 0 | if (!create) { |
2466 | 0 | goto unlock; |
2467 | 0 | } |
2468 | | /* |
2469 | | * Try to upgrade the lock and if that fails unlock then relock. |
2470 | | */ |
2471 | 0 | TREE_FORCEUPGRADE(&qpdb->tree_lock, &tlocktype); |
2472 | 0 | result = dns_qp_getname(qpdb->tree, name, nspace, |
2473 | 0 | (void **)&node, NULL); |
2474 | 0 | if (result != ISC_R_SUCCESS) { |
2475 | 0 | node = new_qpcnode(qpdb, name, nspace); |
2476 | 0 | result = dns_qp_insert(qpdb->tree, node, 0); |
2477 | 0 | INSIST(result == ISC_R_SUCCESS); |
2478 | 0 | qpcnode_unref(node); |
2479 | 0 | } |
2480 | 0 | } |
2481 | | |
2482 | 0 | reactivate_node(qpdb, node, tlocktype DNS__DB_FLARG_PASS); |
2483 | |
|
2484 | 0 | *nodep = (dns_dbnode_t *)node; |
2485 | 0 | unlock: |
2486 | 0 | TREE_UNLOCK(&qpdb->tree_lock, &tlocktype); |
2487 | |
|
2488 | 0 | return result; |
2489 | 0 | } |
2490 | | |
2491 | | static isc_result_t |
2492 | | qpcache_createiterator(dns_db_t *db, unsigned int options ISC_ATTR_UNUSED, |
2493 | 0 | dns_dbiterator_t **iteratorp) { |
2494 | 0 | qpcache_t *qpdb = (qpcache_t *)db; |
2495 | 0 | qpc_dbit_t *qpdbiter = NULL; |
2496 | |
|
2497 | 0 | REQUIRE(VALID_QPDB(qpdb)); |
2498 | |
|
2499 | 0 | qpdbiter = isc_mem_get(qpdb->common.mctx, sizeof(*qpdbiter)); |
2500 | 0 | *qpdbiter = (qpc_dbit_t){ |
2501 | 0 | .common.methods = &dbiterator_methods, |
2502 | 0 | .common.magic = DNS_DBITERATOR_MAGIC, |
2503 | 0 | .paused = true, |
2504 | 0 | }; |
2505 | |
|
2506 | 0 | qpdbiter->name = dns_fixedname_initname(&qpdbiter->fixed); |
2507 | 0 | dns_db_attach(db, &qpdbiter->common.db); |
2508 | 0 | dns_qpiter_init(qpdb->tree, &qpdbiter->iter); |
2509 | |
|
2510 | 0 | *iteratorp = (dns_dbiterator_t *)qpdbiter; |
2511 | 0 | return ISC_R_SUCCESS; |
2512 | 0 | } |
2513 | | |
2514 | | static isc_result_t |
2515 | | qpcache_allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, |
2516 | | unsigned int options, isc_stdtime_t __now, |
2517 | 0 | dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) { |
2518 | 0 | qpcache_t *qpdb = (qpcache_t *)db; |
2519 | 0 | qpcnode_t *qpnode = (qpcnode_t *)node; |
2520 | 0 | qpc_rditer_t *iterator = NULL; |
2521 | |
|
2522 | 0 | REQUIRE(VALID_QPDB(qpdb)); |
2523 | 0 | REQUIRE(version == NULL); |
2524 | |
|
2525 | 0 | iterator = isc_mem_get(qpdb->common.mctx, sizeof(*iterator)); |
2526 | 0 | *iterator = (qpc_rditer_t){ |
2527 | 0 | .common.magic = DNS_RDATASETITER_MAGIC, |
2528 | 0 | .common.methods = &rdatasetiter_methods, |
2529 | 0 | .common.db = db, |
2530 | 0 | .common.node = node, |
2531 | 0 | .common.options = options, |
2532 | 0 | .common.now = __now ? __now : isc_stdtime_now(), |
2533 | 0 | }; |
2534 | |
|
2535 | 0 | qpcnode_acquire(qpdb, qpnode, isc_rwlocktype_none, |
2536 | 0 | isc_rwlocktype_none DNS__DB_FLARG_PASS); |
2537 | |
|
2538 | 0 | *iteratorp = (dns_rdatasetiter_t *)iterator; |
2539 | |
|
2540 | 0 | return ISC_R_SUCCESS; |
2541 | 0 | } |
2542 | | |
2543 | | static bool |
2544 | 0 | overmaxtype(qpcache_t *qpdb, uint32_t ntypes) { |
2545 | 0 | if (qpdb->maxtypepername == 0) { |
2546 | 0 | return false; |
2547 | 0 | } |
2548 | | |
2549 | 0 | return ntypes >= qpdb->maxtypepername; |
2550 | 0 | } |
2551 | | |
2552 | | static bool |
2553 | 0 | prio_header(dns_slabtop_t *top) { |
2554 | 0 | return prio_type(top->typepair); |
2555 | 0 | } |
2556 | | |
2557 | | static void |
2558 | 0 | qpcnode_attachnode(dns_dbnode_t *source, dns_dbnode_t **targetp DNS__DB_FLARG) { |
2559 | 0 | REQUIRE(targetp != NULL && *targetp == NULL); |
2560 | |
|
2561 | 0 | qpcnode_t *node = (qpcnode_t *)source; |
2562 | 0 | qpcache_t *qpdb = (qpcache_t *)node->qpdb; |
2563 | |
|
2564 | 0 | qpcnode_acquire(qpdb, node, isc_rwlocktype_none, |
2565 | 0 | isc_rwlocktype_none DNS__DB_FLARG_PASS); |
2566 | |
|
2567 | 0 | *targetp = source; |
2568 | 0 | } |
2569 | | |
2570 | | static void |
2571 | 0 | qpcnode_detachnode(dns_dbnode_t **nodep DNS__DB_FLARG) { |
2572 | 0 | qpcnode_t *node = NULL; |
2573 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
2574 | 0 | isc_rwlocktype_t tlocktype = isc_rwlocktype_none; |
2575 | 0 | isc_rwlock_t *nlock = NULL; |
2576 | |
|
2577 | 0 | REQUIRE(nodep != NULL && *nodep != NULL); |
2578 | |
|
2579 | 0 | node = (qpcnode_t *)(*nodep); |
2580 | 0 | qpcache_t *qpdb = (qpcache_t *)node->qpdb; |
2581 | 0 | *nodep = NULL; |
2582 | 0 | nlock = &qpdb->buckets[node->locknum].lock; |
2583 | |
|
2584 | 0 | REQUIRE(VALID_QPDB(qpdb)); |
2585 | | |
2586 | | /* |
2587 | | * We can't destroy qpcache while holding a nodelock, so we need to |
2588 | | * reference it before acquiring the lock and release it afterward. |
2589 | | * Additionally, we must ensure that we don't destroy the database while |
2590 | | * the NODE_LOCK is locked. |
2591 | | */ |
2592 | 0 | qpcache_ref(qpdb); |
2593 | |
|
2594 | 0 | rcu_read_lock(); |
2595 | 0 | NODE_RDLOCK(nlock, &nlocktype); |
2596 | 0 | qpcnode_release(qpdb, node, &nlocktype, &tlocktype DNS__DB_FLARG_PASS); |
2597 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
2598 | 0 | rcu_read_unlock(); |
2599 | |
|
2600 | 0 | qpcache_detach(&qpdb); |
2601 | 0 | } |
2602 | | |
2603 | | static isc_result_t |
2604 | | expire_ncache_entry(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabtop_t *top, |
2605 | | dns_slabheader_t *newheader, dns_trust_t trust, |
2606 | | dns_rdataset_t *addedrdataset, isc_stdtime_t now, |
2607 | | isc_rwlocktype_t nlocktype, |
2608 | 0 | isc_rwlocktype_t tlocktype DNS__DB_FLARG) { |
2609 | 0 | dns_rdatatype_t rdtype = DNS_TYPEPAIR_TYPE(newheader->typepair); |
2610 | 0 | dns_rdatatype_t covers = DNS_TYPEPAIR_COVERS(newheader->typepair); |
2611 | 0 | dns_typepair_t sigpair = !dns_rdatatype_issig(rdtype) |
2612 | 0 | ? DNS_SIGTYPEPAIR(rdtype) |
2613 | 0 | : dns_typepair_none; |
2614 | | /* |
2615 | | * 1. If we find a cached NXDOMAIN, don't cache anything else |
2616 | | * (dns_typepair_any). |
2617 | | * |
2618 | | * 2. Don't cache an RRSIG if it covers a type for which we have a |
2619 | | * cached NODATA record. |
2620 | | */ |
2621 | 0 | if ((top->typepair == dns_typepair_any) || |
2622 | 0 | (sigpair != dns_rdatatype_none && newheader->typepair == sigpair && |
2623 | 0 | DNS_TYPEPAIR_TYPE(top->typepair) == covers)) |
2624 | 0 | { |
2625 | 0 | dns_slabheader_t *header = first_header(top); |
2626 | 0 | if (header == NULL) { |
2627 | 0 | return DNS_R_CONTINUE; |
2628 | 0 | } |
2629 | | |
2630 | 0 | if (trust < header->trust) { |
2631 | | /* |
2632 | | * The NXDOMAIN/NODATA(QTYPE=ANY) is more trusted. |
2633 | | */ |
2634 | 0 | qpcache_hit(qpdb, header); |
2635 | 0 | bindrdataset(qpdb, qpnode, header, now, nlocktype, |
2636 | 0 | tlocktype, |
2637 | 0 | addedrdataset DNS__DB_FLARG_PASS); |
2638 | 0 | return DNS_R_UNCHANGED; |
2639 | 0 | } |
2640 | | |
2641 | | /* |
2642 | | * The new rdataset is better. Expire the ncache entry. |
2643 | | */ |
2644 | 0 | mark_ancient(header); |
2645 | 0 | return DNS_R_CONTINUE; |
2646 | 0 | } |
2647 | | |
2648 | 0 | return DNS_R_CONTINUE; |
2649 | 0 | } |
2650 | | |
2651 | | static isc_result_t |
2652 | | add(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *newheader, |
2653 | | unsigned int options, dns_rdataset_t *addedrdataset, isc_stdtime_t now, |
2654 | 0 | isc_rwlocktype_t nlocktype, isc_rwlocktype_t tlocktype DNS__DB_FLARG) { |
2655 | 0 | dns_slabtop_t *priotop = NULL, *expiretop = NULL; |
2656 | 0 | dns_slabtop_t *oldtop = NULL, *related = NULL; |
2657 | 0 | dns_trust_t trust; |
2658 | 0 | uint32_t ntypes = 0; |
2659 | 0 | dns_rdatatype_t rdtype = DNS_TYPEPAIR_TYPE(newheader->typepair); |
2660 | 0 | dns_rdatatype_t covers = DNS_TYPEPAIR_COVERS(newheader->typepair); |
2661 | |
|
2662 | 0 | REQUIRE(rdtype != dns_rdatatype_none); |
2663 | 0 | if (dns_rdatatype_issig(rdtype)) { |
2664 | | /* signature must be either negative or cover something */ |
2665 | 0 | REQUIRE(NEGATIVE(newheader) || covers != dns_rdatatype_none); |
2666 | 0 | } else { |
2667 | | /* non-signature it must cover nothing */ |
2668 | 0 | REQUIRE(covers == dns_rdatatype_none); |
2669 | 0 | } |
2670 | | /* positive header can't be for type ANY */ |
2671 | 0 | REQUIRE(rdtype != dns_rdatatype_any || NEGATIVE(newheader)); |
2672 | |
|
2673 | 0 | if ((options & DNS_DBADD_FORCE) != 0) { |
2674 | 0 | trust = dns_trust_ultimate; |
2675 | 0 | } else { |
2676 | 0 | trust = newheader->trust; |
2677 | 0 | } |
2678 | |
|
2679 | 0 | DNS_SLABTOP_FOREACH(top, qpnode->data) { |
2680 | 0 | dns_slabheader_t *header = first_header(top); |
2681 | 0 | if (header == NULL) { |
2682 | 0 | continue; |
2683 | 0 | } |
2684 | | |
2685 | 0 | if (EXISTS(newheader) && NEGATIVE(newheader) && |
2686 | 0 | rdtype == dns_rdatatype_any) |
2687 | 0 | { |
2688 | | /* |
2689 | | * We're adding a negative cache entry which |
2690 | | * covers all types (NXDOMAIN, NODATA(QTYPE=ANY)). |
2691 | | * |
2692 | | * Make all other data ancient so that the only |
2693 | | * rdataset that can be found at this node is the |
2694 | | * negative cache entry. |
2695 | | */ |
2696 | 0 | mark_ancient(header); |
2697 | 0 | } |
2698 | |
|
2699 | 0 | if (EXISTS(newheader) && NEGATIVE(newheader) && |
2700 | 0 | rdtype == dns_rdatatype_rrsig) |
2701 | 0 | { |
2702 | | /* |
2703 | | * We're adding a proof that a signature doesn't exist. |
2704 | | * |
2705 | | * Mark all existing signatures as ancient. |
2706 | | */ |
2707 | 0 | if (DNS_TYPEPAIR_TYPE(top->typepair) == |
2708 | 0 | dns_rdatatype_rrsig) |
2709 | 0 | { |
2710 | 0 | mark_ancient(header); |
2711 | 0 | } |
2712 | 0 | } |
2713 | |
|
2714 | 0 | if (EXISTS(newheader) && !NEGATIVE(newheader) && |
2715 | 0 | NEGATIVE(header) && EXISTS(header) && ACTIVE(header, now)) |
2716 | 0 | { |
2717 | | /* |
2718 | | * Look for existing active NXDOMAIN or negative |
2719 | | * covered type if we are adding RRSIG. |
2720 | | */ |
2721 | 0 | isc_result_t result = expire_ncache_entry( |
2722 | 0 | qpdb, qpnode, top, newheader, trust, |
2723 | 0 | addedrdataset, now, nlocktype, tlocktype); |
2724 | 0 | if (result == DNS_R_UNCHANGED) { |
2725 | | /* |
2726 | | * The existing negative entry is more trusted |
2727 | | * than the new rdataset. |
2728 | | */ |
2729 | 0 | return DNS_R_UNCHANGED; |
2730 | 0 | } |
2731 | 0 | INSIST(result == DNS_R_CONTINUE); |
2732 | 0 | } |
2733 | | |
2734 | 0 | if (ACTIVE(header, now)) { |
2735 | 0 | ++ntypes; |
2736 | 0 | expiretop = top; |
2737 | 0 | } |
2738 | 0 | if (prio_header(top)) { |
2739 | 0 | priotop = top; |
2740 | 0 | } |
2741 | |
|
2742 | 0 | if (top->typepair == newheader->typepair) { |
2743 | 0 | INSIST(oldtop == NULL); |
2744 | 0 | oldtop = top; |
2745 | 0 | } |
2746 | |
|
2747 | 0 | if (rdtype == dns_rdatatype_rrsig) { |
2748 | 0 | if (DNS_TYPEPAIR_TYPE(top->typepair) == covers) { |
2749 | 0 | INSIST(related == NULL); |
2750 | 0 | related = top; |
2751 | 0 | } |
2752 | 0 | } else { |
2753 | 0 | if (top->typepair == DNS_SIGTYPEPAIR(rdtype)) { |
2754 | 0 | INSIST(related == NULL); |
2755 | 0 | related = top; |
2756 | 0 | } |
2757 | 0 | } |
2758 | 0 | } |
2759 | | |
2760 | 0 | if (oldtop != NULL) { |
2761 | 0 | dns_slabheader_t *oldheader = first_header(oldtop); |
2762 | 0 | INSIST(oldheader != NULL); |
2763 | | |
2764 | | /* |
2765 | | * Deleting an already non-existent rdataset has no effect. |
2766 | | */ |
2767 | 0 | if (!EXISTS(oldheader) && !EXISTS(newheader)) { |
2768 | 0 | return DNS_R_UNCHANGED; |
2769 | 0 | } |
2770 | | |
2771 | | /* |
2772 | | * Trying to add an rdataset with lower trust to a cache |
2773 | | * DB has no effect, provided that the cache data isn't |
2774 | | * stale. If the cache data is stale, new lower trust |
2775 | | * data will supersede it below. Unclear what the best |
2776 | | * policy is here. |
2777 | | */ |
2778 | 0 | dns_trust_t oldtrust = atomic_load(&oldheader->trust); |
2779 | 0 | if (trust < oldtrust && |
2780 | 0 | (ACTIVE(oldheader, now) || !EXISTS(oldheader))) |
2781 | 0 | { |
2782 | 0 | qpcache_hit(qpdb, oldheader); |
2783 | 0 | bindrdataset(qpdb, qpnode, oldheader, now, nlocktype, |
2784 | 0 | tlocktype, |
2785 | 0 | addedrdataset DNS__DB_FLARG_PASS); |
2786 | 0 | if (ACTIVE(oldheader, now) && |
2787 | 0 | (options & DNS_DBADD_EQUALOK) != 0 && |
2788 | 0 | dns_rdataslab_equalx( |
2789 | 0 | oldheader, newheader, qpdb->common.rdclass, |
2790 | 0 | DNS_TYPEPAIR_TYPE(oldtop->typepair))) |
2791 | 0 | { |
2792 | | /* |
2793 | | * Updated by caller to ISC_R_SUCCESS after |
2794 | | * cleaning up newheader. |
2795 | | */ |
2796 | 0 | return ISC_R_EXISTS; |
2797 | 0 | } |
2798 | 0 | return DNS_R_UNCHANGED; |
2799 | 0 | } |
2800 | | |
2801 | | /* |
2802 | | * Don't replace existing NS in the cache if they already exist |
2803 | | * and replacing the existing one would increase the TTL. This |
2804 | | * prevents named being locked to old servers. Don't lower trust |
2805 | | * of existing record if the update is forced. Nothing special |
2806 | | * to be done w.r.t stale data; it gets replaced normally |
2807 | | * further down. |
2808 | | */ |
2809 | 0 | if (ACTIVE(oldheader, now) && |
2810 | 0 | oldtop->typepair == DNS_TYPEPAIR(dns_rdatatype_ns) && |
2811 | 0 | EXISTS(oldheader) && EXISTS(newheader) && |
2812 | 0 | newheader->trust < oldtrust && |
2813 | 0 | oldheader->expire < newheader->expire && |
2814 | 0 | dns_rdataslab_equalx(oldheader, newheader, |
2815 | 0 | qpdb->common.rdclass, |
2816 | 0 | DNS_TYPEPAIR_TYPE(oldtop->typepair))) |
2817 | 0 | { |
2818 | 0 | if (oldheader->noqname == NULL && |
2819 | 0 | newheader->noqname != NULL) |
2820 | 0 | { |
2821 | 0 | oldheader->noqname = newheader->noqname; |
2822 | 0 | newheader->noqname = NULL; |
2823 | 0 | } |
2824 | 0 | if (oldheader->closest == NULL && |
2825 | 0 | newheader->closest != NULL) |
2826 | 0 | { |
2827 | 0 | oldheader->closest = newheader->closest; |
2828 | 0 | newheader->closest = NULL; |
2829 | 0 | } |
2830 | |
|
2831 | 0 | qpcache_hit(qpdb, oldheader); |
2832 | 0 | bindrdataset(qpdb, qpnode, oldheader, now, nlocktype, |
2833 | 0 | tlocktype, |
2834 | 0 | addedrdataset DNS__DB_FLARG_PASS); |
2835 | 0 | if ((options & DNS_DBADD_EQUALOK) != 0) { |
2836 | | /* |
2837 | | * Updated by caller to ISC_R_SUCCESS after |
2838 | | * cleaning up newheader. |
2839 | | */ |
2840 | 0 | return ISC_R_EXISTS; |
2841 | 0 | } |
2842 | 0 | return DNS_R_UNCHANGED; |
2843 | 0 | } |
2844 | | |
2845 | | /* |
2846 | | * If we will be replacing an NS RRset, force its TTL |
2847 | | * to be no more than the current NS RRset's TTL. This |
2848 | | * ensures the delegations that are withdrawn are honoured. |
2849 | | */ |
2850 | 0 | if (ACTIVE(oldheader, now) && |
2851 | 0 | oldtop->typepair == DNS_TYPEPAIR(dns_rdatatype_ns) && |
2852 | 0 | EXISTS(oldheader) && EXISTS(newheader) && |
2853 | 0 | newheader->trust > oldtrust) |
2854 | 0 | { |
2855 | 0 | if (newheader->expire > oldheader->expire) { |
2856 | 0 | if (ZEROTTL(oldheader)) { |
2857 | 0 | DNS_SLABHEADER_SETATTR( |
2858 | 0 | newheader, |
2859 | 0 | DNS_SLABHEADERATTR_ZEROTTL); |
2860 | 0 | } |
2861 | 0 | newheader->expire = oldheader->expire; |
2862 | 0 | } |
2863 | 0 | } |
2864 | 0 | if (ACTIVE(oldheader, now) && |
2865 | 0 | (options & DNS_DBADD_PREFETCH) == 0 && |
2866 | 0 | (oldtop->typepair == DNS_TYPEPAIR(dns_rdatatype_a) || |
2867 | 0 | oldtop->typepair == DNS_TYPEPAIR(dns_rdatatype_aaaa) || |
2868 | 0 | oldtop->typepair == DNS_TYPEPAIR(dns_rdatatype_ds) || |
2869 | 0 | oldtop->typepair == DNS_SIGTYPEPAIR(dns_rdatatype_ds)) && |
2870 | 0 | EXISTS(oldheader) && EXISTS(newheader) && |
2871 | 0 | newheader->trust < oldtrust && |
2872 | 0 | oldheader->expire < newheader->expire && |
2873 | 0 | dns_rdataslab_equal(oldheader, newheader)) |
2874 | 0 | { |
2875 | 0 | if (oldheader->noqname == NULL && |
2876 | 0 | newheader->noqname != NULL) |
2877 | 0 | { |
2878 | 0 | oldheader->noqname = newheader->noqname; |
2879 | 0 | newheader->noqname = NULL; |
2880 | 0 | } |
2881 | 0 | if (oldheader->closest == NULL && |
2882 | 0 | newheader->closest != NULL) |
2883 | 0 | { |
2884 | 0 | oldheader->closest = newheader->closest; |
2885 | 0 | newheader->closest = NULL; |
2886 | 0 | } |
2887 | |
|
2888 | 0 | qpcache_hit(qpdb, oldheader); |
2889 | 0 | bindrdataset(qpdb, qpnode, oldheader, now, nlocktype, |
2890 | 0 | tlocktype, |
2891 | 0 | addedrdataset DNS__DB_FLARG_PASS); |
2892 | 0 | if ((options & DNS_DBADD_EQUALOK) != 0) { |
2893 | | /* |
2894 | | * Updated by caller to ISC_R_SUCCESS after |
2895 | | * cleaning up newheader. |
2896 | | */ |
2897 | 0 | return ISC_R_EXISTS; |
2898 | 0 | } |
2899 | 0 | return DNS_R_UNCHANGED; |
2900 | 0 | } |
2901 | | |
2902 | 0 | newheader->top = oldheader->top; |
2903 | 0 | cds_list_add(&newheader->headers_link, |
2904 | 0 | &oldheader->top->headers); |
2905 | |
|
2906 | 0 | if (ISC_SIEVE_LINKED(oldheader->top, link)) { |
2907 | 0 | ISC_SIEVE_UNLINK(qpdb->buckets[qpnode->locknum].sieve, |
2908 | 0 | oldheader->top, link); |
2909 | 0 | } |
2910 | |
|
2911 | 0 | qpcache_miss(qpdb, newheader, &nlocktype, |
2912 | 0 | &tlocktype DNS__DB_FLARG_PASS); |
2913 | |
|
2914 | 0 | mark_ancient(oldheader); |
2915 | |
|
2916 | 0 | if (EXISTS(newheader) && NEGATIVE(newheader) && |
2917 | 0 | !dns_rdatatype_issig(rdtype)) |
2918 | 0 | { |
2919 | 0 | if (oldtop->related != NULL) { |
2920 | 0 | dns_slabheader_t *oldsigheader = |
2921 | 0 | first_header(oldtop->related); |
2922 | 0 | mark_ancient(oldsigheader); |
2923 | 0 | } |
2924 | 0 | } |
2925 | 0 | } else if (!EXISTS(newheader)) { |
2926 | | /* |
2927 | | * The type already doesn't exist; no point trying |
2928 | | * to delete it. |
2929 | | */ |
2930 | 0 | return DNS_R_UNCHANGED; |
2931 | 0 | } else { |
2932 | | /* No rdatasets of the given type exist at the node. */ |
2933 | 0 | dns_slabtop_t *newtop = dns_slabtop_new( |
2934 | 0 | ((dns_db_t *)qpdb)->mctx, newheader->typepair); |
2935 | |
|
2936 | 0 | if (prio_header(newtop)) { |
2937 | | /* This is a priority type, prepend it */ |
2938 | 0 | cds_list_add(&newtop->types_link, qpnode->data); |
2939 | 0 | } else if (priotop != NULL) { |
2940 | | /* Append after the priority headers */ |
2941 | 0 | cds_list_add(&newtop->types_link, &priotop->types_link); |
2942 | 0 | } else { |
2943 | | /* There were no priority headers */ |
2944 | 0 | cds_list_add(&newtop->types_link, qpnode->data); |
2945 | 0 | } |
2946 | |
|
2947 | 0 | if (related != NULL) { |
2948 | 0 | INSIST(related->related == NULL); |
2949 | 0 | related->related = newtop; |
2950 | 0 | newtop->related = related; |
2951 | 0 | } |
2952 | |
|
2953 | 0 | newheader->top = newtop; |
2954 | 0 | cds_list_add(&newheader->headers_link, &newtop->headers); |
2955 | |
|
2956 | 0 | qpcache_miss(qpdb, newheader, &nlocktype, |
2957 | 0 | &tlocktype DNS__DB_FLARG_PASS); |
2958 | |
|
2959 | 0 | if (overmaxtype(qpdb, ntypes)) { |
2960 | 0 | if (expiretop == NULL) { |
2961 | 0 | expiretop = newtop; |
2962 | 0 | } |
2963 | 0 | if (NEGATIVE(newheader) && !prio_header(newtop)) { |
2964 | | /* |
2965 | | * Add the new non-priority negative |
2966 | | * header to the database only |
2967 | | * temporarily. |
2968 | | */ |
2969 | 0 | expiretop = newtop; |
2970 | 0 | } |
2971 | |
|
2972 | 0 | mark_ancient(first_header(expiretop)); |
2973 | 0 | if (expiretop->related != NULL) { |
2974 | 0 | mark_ancient(first_header(expiretop->related)); |
2975 | 0 | } |
2976 | 0 | } |
2977 | 0 | } |
2978 | | |
2979 | 0 | bindrdataset(qpdb, qpnode, newheader, now, nlocktype, tlocktype, |
2980 | 0 | addedrdataset DNS__DB_FLARG_PASS); |
2981 | |
|
2982 | 0 | return ISC_R_SUCCESS; |
2983 | 0 | } |
2984 | | |
2985 | | static isc_result_t |
2986 | | addnoqname(isc_mem_t *mctx, dns_slabheader_t *newheader, uint32_t maxrrperset, |
2987 | 0 | dns_rdataset_t *rdataset) { |
2988 | 0 | isc_result_t result; |
2989 | 0 | dns_slabheader_proof_t *noqname = NULL; |
2990 | 0 | dns_name_t name = DNS_NAME_INITEMPTY; |
2991 | 0 | dns_rdataset_t neg = DNS_RDATASET_INIT, negsig = DNS_RDATASET_INIT; |
2992 | 0 | isc_region_t r1, r2; |
2993 | |
|
2994 | 0 | result = dns_rdataset_getnoqname(rdataset, &name, &neg, &negsig); |
2995 | 0 | RUNTIME_CHECK(result == ISC_R_SUCCESS); |
2996 | |
|
2997 | 0 | result = dns_rdataslab_fromrdataset(&neg, mctx, &r1, maxrrperset); |
2998 | 0 | if (result != ISC_R_SUCCESS) { |
2999 | 0 | goto cleanup; |
3000 | 0 | } |
3001 | | |
3002 | 0 | result = dns_rdataslab_fromrdataset(&negsig, mctx, &r2, maxrrperset); |
3003 | 0 | if (result != ISC_R_SUCCESS) { |
3004 | 0 | goto cleanup; |
3005 | 0 | } |
3006 | | |
3007 | 0 | noqname = isc_mem_get(mctx, sizeof(*noqname)); |
3008 | 0 | *noqname = (dns_slabheader_proof_t){ |
3009 | 0 | .neg = ((dns_slabheader_t *)r1.base)->raw, |
3010 | 0 | .negsig = ((dns_slabheader_t *)r2.base)->raw, |
3011 | 0 | .type = neg.type, |
3012 | 0 | .name = DNS_NAME_INITEMPTY, |
3013 | 0 | }; |
3014 | 0 | dns_name_dup(&name, mctx, &noqname->name); |
3015 | 0 | newheader->noqname = noqname; |
3016 | |
|
3017 | 0 | cleanup: |
3018 | 0 | dns_rdataset_disassociate(&neg); |
3019 | 0 | dns_rdataset_disassociate(&negsig); |
3020 | |
|
3021 | 0 | return result; |
3022 | 0 | } |
3023 | | |
3024 | | static isc_result_t |
3025 | | addclosest(isc_mem_t *mctx, dns_slabheader_t *newheader, uint32_t maxrrperset, |
3026 | 0 | dns_rdataset_t *rdataset) { |
3027 | 0 | isc_result_t result; |
3028 | 0 | dns_slabheader_proof_t *closest = NULL; |
3029 | 0 | dns_name_t name = DNS_NAME_INITEMPTY; |
3030 | 0 | dns_rdataset_t neg = DNS_RDATASET_INIT, negsig = DNS_RDATASET_INIT; |
3031 | 0 | isc_region_t r1, r2; |
3032 | |
|
3033 | 0 | result = dns_rdataset_getclosest(rdataset, &name, &neg, &negsig); |
3034 | 0 | RUNTIME_CHECK(result == ISC_R_SUCCESS); |
3035 | |
|
3036 | 0 | result = dns_rdataslab_fromrdataset(&neg, mctx, &r1, maxrrperset); |
3037 | 0 | if (result != ISC_R_SUCCESS) { |
3038 | 0 | goto cleanup; |
3039 | 0 | } |
3040 | | |
3041 | 0 | result = dns_rdataslab_fromrdataset(&negsig, mctx, &r2, maxrrperset); |
3042 | 0 | if (result != ISC_R_SUCCESS) { |
3043 | 0 | goto cleanup; |
3044 | 0 | } |
3045 | | |
3046 | 0 | closest = isc_mem_get(mctx, sizeof(*closest)); |
3047 | 0 | *closest = (dns_slabheader_proof_t){ |
3048 | 0 | .neg = ((dns_slabheader_t *)r1.base)->raw, |
3049 | 0 | .negsig = ((dns_slabheader_t *)r2.base)->raw, |
3050 | 0 | .name = DNS_NAME_INITEMPTY, |
3051 | 0 | .type = neg.type, |
3052 | 0 | }; |
3053 | 0 | dns_name_dup(&name, mctx, &closest->name); |
3054 | 0 | newheader->closest = closest; |
3055 | |
|
3056 | 0 | cleanup: |
3057 | 0 | dns_rdataset_disassociate(&neg); |
3058 | 0 | dns_rdataset_disassociate(&negsig); |
3059 | 0 | return result; |
3060 | 0 | } |
3061 | | |
3062 | | static void |
3063 | | expire_ttl_headers(qpcache_t *qpdb, unsigned int locknum, |
3064 | | isc_rwlocktype_t *nlocktypep, isc_rwlocktype_t *tlocktypep, |
3065 | | isc_stdtime_t now DNS__DB_FLARG); |
3066 | | |
3067 | | static isc_result_t |
3068 | | qpcache_addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, |
3069 | | isc_stdtime_t __now, dns_rdataset_t *rdataset, |
3070 | | unsigned int options, |
3071 | 0 | dns_rdataset_t *addedrdataset DNS__DB_FLARG) { |
3072 | 0 | qpcache_t *qpdb = (qpcache_t *)db; |
3073 | 0 | qpcnode_t *qpnode = (qpcnode_t *)node; |
3074 | 0 | isc_region_t region; |
3075 | 0 | dns_slabheader_t *newheader = NULL; |
3076 | 0 | isc_result_t result; |
3077 | 0 | bool delegating = false; |
3078 | 0 | bool newnsec = false; |
3079 | 0 | isc_rwlocktype_t tlocktype = isc_rwlocktype_none; |
3080 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
3081 | 0 | isc_rwlock_t *nlock = NULL; |
3082 | 0 | dns_fixedname_t fixed; |
3083 | 0 | dns_name_t *name = NULL; |
3084 | 0 | isc_stdtime_t now = __now ? __now : isc_stdtime_now(); |
3085 | |
|
3086 | 0 | REQUIRE(VALID_QPDB(qpdb)); |
3087 | 0 | REQUIRE(version == NULL); |
3088 | |
|
3089 | 0 | result = dns_rdataslab_fromrdataset(rdataset, qpnode->mctx, ®ion, |
3090 | 0 | qpdb->maxrrperset); |
3091 | 0 | if (result != ISC_R_SUCCESS) { |
3092 | 0 | if (result == DNS_R_TOOMANYRECORDS) { |
3093 | 0 | dns__db_logtoomanyrecords((dns_db_t *)qpdb, |
3094 | 0 | &qpnode->name, rdataset->type, |
3095 | 0 | "adding", qpdb->maxrrperset); |
3096 | 0 | } |
3097 | 0 | return result; |
3098 | 0 | } |
3099 | | |
3100 | 0 | name = dns_fixedname_initname(&fixed); |
3101 | 0 | dns_name_copy(&qpnode->name, name); |
3102 | 0 | dns_rdataset_getownercase(rdataset, name); |
3103 | |
|
3104 | 0 | newheader = (dns_slabheader_t *)region.base; |
3105 | 0 | dns_slabheader_reset(newheader, node); |
3106 | | |
3107 | | /* |
3108 | | * By default, dns_rdataslab_fromrdataset() sets newheader->ttl |
3109 | | * to the rdataset TTL. In the case of the cache, that's wrong; |
3110 | | * we need it to be set to the expire time instead. |
3111 | | */ |
3112 | 0 | setttl(newheader, rdataset->ttl + now); |
3113 | 0 | if (rdataset->ttl == 0U) { |
3114 | 0 | DNS_SLABHEADER_SETATTR(newheader, DNS_SLABHEADERATTR_ZEROTTL); |
3115 | 0 | } |
3116 | |
|
3117 | 0 | if (rdataset->attributes.prefetch) { |
3118 | 0 | DNS_SLABHEADER_SETATTR(newheader, DNS_SLABHEADERATTR_PREFETCH); |
3119 | 0 | } |
3120 | 0 | if (rdataset->attributes.negative) { |
3121 | 0 | DNS_SLABHEADER_SETATTR(newheader, DNS_SLABHEADERATTR_NEGATIVE); |
3122 | 0 | } |
3123 | 0 | if (rdataset->attributes.nxdomain) { |
3124 | 0 | DNS_SLABHEADER_SETATTR(newheader, DNS_SLABHEADERATTR_NXDOMAIN); |
3125 | 0 | } |
3126 | 0 | if (rdataset->attributes.optout) { |
3127 | 0 | DNS_SLABHEADER_SETATTR(newheader, DNS_SLABHEADERATTR_OPTOUT); |
3128 | 0 | } |
3129 | 0 | if (rdataset->attributes.noqname) { |
3130 | 0 | result = addnoqname(qpnode->mctx, newheader, qpdb->maxrrperset, |
3131 | 0 | rdataset); |
3132 | 0 | if (result != ISC_R_SUCCESS) { |
3133 | 0 | return result; |
3134 | 0 | } |
3135 | 0 | } |
3136 | 0 | if (rdataset->attributes.closest) { |
3137 | 0 | result = addclosest(qpnode->mctx, newheader, qpdb->maxrrperset, |
3138 | 0 | rdataset); |
3139 | 0 | if (result != ISC_R_SUCCESS) { |
3140 | 0 | return result; |
3141 | 0 | } |
3142 | 0 | } |
3143 | | |
3144 | 0 | nlock = &qpdb->buckets[qpnode->locknum].lock; |
3145 | | |
3146 | | /* |
3147 | | * If we're adding a delegation type (which would be an NS or DNAME |
3148 | | * for a zone, but only DNAME counts for a cache), we need to set |
3149 | | * the callback bit on the node. |
3150 | | */ |
3151 | 0 | if (rdataset->type == dns_rdatatype_dname) { |
3152 | 0 | delegating = true; |
3153 | 0 | } |
3154 | | |
3155 | | /* |
3156 | | * Add to the auxiliary NSEC tree if we're adding an NSEC record. |
3157 | | */ |
3158 | 0 | if (rdataset->type == dns_rdatatype_nsec) { |
3159 | 0 | NODE_RDLOCK(nlock, &nlocktype); |
3160 | 0 | if (!qpnode->havensec) { |
3161 | 0 | newnsec = true; |
3162 | 0 | } |
3163 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
3164 | 0 | } |
3165 | | |
3166 | | /* |
3167 | | * If we're adding a delegation type or adding to the auxiliary |
3168 | | * NSEC tree, hold an exclusive lock on the tree. |
3169 | | */ |
3170 | 0 | if (delegating || newnsec) { |
3171 | 0 | TREE_WRLOCK(&qpdb->tree_lock, &tlocktype); |
3172 | 0 | } |
3173 | | |
3174 | 0 | NODE_WRLOCK(nlock, &nlocktype); |
3175 | |
|
3176 | 0 | expire_ttl_headers(qpdb, qpnode->locknum, &nlocktype, &tlocktype, |
3177 | 0 | now DNS__DB_FLARG_PASS); |
3178 | |
|
3179 | 0 | if (newnsec && !qpnode->havensec) { |
3180 | 0 | qpcnode_t *nsecnode = NULL; |
3181 | |
|
3182 | 0 | result = dns_qp_getname(qpdb->tree, name, DNS_DBNAMESPACE_NSEC, |
3183 | 0 | (void **)&nsecnode, NULL); |
3184 | 0 | if (result != ISC_R_SUCCESS) { |
3185 | 0 | INSIST(nsecnode == NULL); |
3186 | 0 | nsecnode = new_qpcnode(qpdb, name, |
3187 | 0 | DNS_DBNAMESPACE_NSEC); |
3188 | 0 | result = dns_qp_insert(qpdb->tree, nsecnode, 0); |
3189 | 0 | INSIST(result == ISC_R_SUCCESS); |
3190 | 0 | qpcnode_detach(&nsecnode); |
3191 | 0 | } |
3192 | 0 | qpnode->havensec = true; |
3193 | 0 | } |
3194 | |
|
3195 | 0 | result = add(qpdb, qpnode, newheader, options, addedrdataset, now, |
3196 | 0 | nlocktype, tlocktype DNS__DB_FLARG_PASS); |
3197 | |
|
3198 | 0 | if (result == ISC_R_SUCCESS) { |
3199 | 0 | DNS_SLABHEADER_SETATTR(newheader, DNS_SLABHEADERATTR_STATCOUNT); |
3200 | 0 | update_rrsetstats(qpdb->rrsetstats, newheader->typepair, |
3201 | 0 | newheader->attributes, true); |
3202 | |
|
3203 | 0 | if (delegating) { |
3204 | 0 | qpnode->delegating = 1; |
3205 | 0 | } |
3206 | 0 | } else { |
3207 | 0 | dns_slabheader_destroy(&newheader); |
3208 | 0 | } |
3209 | |
|
3210 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
3211 | |
|
3212 | 0 | if (result == ISC_R_EXISTS) { |
3213 | 0 | result = ISC_R_SUCCESS; |
3214 | 0 | } |
3215 | |
|
3216 | 0 | if (tlocktype != isc_rwlocktype_none) { |
3217 | 0 | TREE_UNLOCK(&qpdb->tree_lock, &tlocktype); |
3218 | 0 | } |
3219 | | |
3220 | 0 | INSIST(tlocktype == isc_rwlocktype_none); |
3221 | |
|
3222 | 0 | return result; |
3223 | 0 | } |
3224 | | |
3225 | | static isc_result_t |
3226 | | qpcache_deleterdataset(dns_db_t *db, dns_dbnode_t *node, |
3227 | | dns_dbversion_t *version, dns_rdatatype_t type, |
3228 | 0 | dns_rdatatype_t covers DNS__DB_FLARG) { |
3229 | 0 | qpcache_t *qpdb = (qpcache_t *)db; |
3230 | 0 | qpcnode_t *qpnode = (qpcnode_t *)node; |
3231 | 0 | isc_result_t result; |
3232 | 0 | dns_slabheader_t *newheader = NULL; |
3233 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
3234 | 0 | isc_rwlock_t *nlock = NULL; |
3235 | 0 | uint16_t attributes = DNS_SLABHEADERATTR_NONEXISTENT; |
3236 | |
|
3237 | 0 | REQUIRE(VALID_QPDB(qpdb)); |
3238 | 0 | REQUIRE(version == NULL); |
3239 | | |
3240 | | /* Positive ANY type can't be in the cache. */ |
3241 | 0 | if (type == dns_rdatatype_any) { |
3242 | 0 | return ISC_R_NOTIMPLEMENTED; |
3243 | 0 | } |
3244 | | |
3245 | | /* Convert the negative type into positive type. */ |
3246 | 0 | if (type == dns_rdatatype_none && covers != dns_rdatatype_none) { |
3247 | 0 | type = covers; |
3248 | 0 | covers = dns_rdatatype_none; |
3249 | 0 | attributes |= DNS_SLABHEADERATTR_NEGATIVE; |
3250 | 0 | } |
3251 | |
|
3252 | 0 | newheader = dns_slabheader_new(db->mctx, node); |
3253 | 0 | newheader->typepair = DNS_TYPEPAIR_VALUE(type, covers); |
3254 | 0 | setttl(newheader, 0); |
3255 | 0 | atomic_init(&newheader->attributes, attributes); |
3256 | |
|
3257 | 0 | nlock = &qpdb->buckets[qpnode->locknum].lock; |
3258 | 0 | NODE_WRLOCK(nlock, &nlocktype); |
3259 | 0 | result = add(qpdb, qpnode, newheader, DNS_DBADD_FORCE, NULL, 0, |
3260 | 0 | nlocktype, isc_rwlocktype_none DNS__DB_FLARG_PASS); |
3261 | 0 | if (result != ISC_R_SUCCESS) { |
3262 | 0 | dns_slabheader_destroy(&newheader); |
3263 | 0 | } |
3264 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
3265 | |
|
3266 | 0 | return result; |
3267 | 0 | } |
3268 | | |
3269 | | static unsigned int |
3270 | 0 | nodecount(dns_db_t *db) { |
3271 | 0 | qpcache_t *qpdb = (qpcache_t *)db; |
3272 | 0 | dns_qp_memusage_t mu; |
3273 | 0 | isc_rwlocktype_t tlocktype = isc_rwlocktype_none; |
3274 | |
|
3275 | 0 | REQUIRE(VALID_QPDB(qpdb)); |
3276 | |
|
3277 | 0 | TREE_RDLOCK(&qpdb->tree_lock, &tlocktype); |
3278 | 0 | mu = dns_qp_memusage(qpdb->tree); |
3279 | 0 | TREE_UNLOCK(&qpdb->tree_lock, &tlocktype); |
3280 | |
|
3281 | 0 | return mu.leaves; |
3282 | 0 | } |
3283 | | |
3284 | | isc_result_t |
3285 | | dns__qpcache_create(isc_mem_t *mctx, const dns_name_t *origin, |
3286 | | dns_dbtype_t type, dns_rdataclass_t rdclass, |
3287 | | unsigned int argc, char *argv[], |
3288 | 0 | void *driverarg ISC_ATTR_UNUSED, dns_db_t **dbp) { |
3289 | 0 | qpcache_t *qpdb = NULL; |
3290 | 0 | isc_mem_t *hmctx = mctx; |
3291 | 0 | isc_loop_t *loop = isc_loop(); |
3292 | 0 | int i; |
3293 | 0 | size_t nloops = isc_loopmgr_nloops(); |
3294 | | |
3295 | | /* This database implementation only supports cache semantics */ |
3296 | 0 | REQUIRE(type == dns_dbtype_cache); |
3297 | 0 | REQUIRE(loop != NULL); |
3298 | |
|
3299 | 0 | qpdb = isc_mem_get(mctx, |
3300 | 0 | sizeof(*qpdb) + nloops * sizeof(qpdb->buckets[0])); |
3301 | 0 | *qpdb = (qpcache_t){ |
3302 | 0 | .common.methods = &qpdb_cachemethods, |
3303 | 0 | .common.origin = DNS_NAME_INITEMPTY, |
3304 | 0 | .common.rdclass = rdclass, |
3305 | 0 | .common.attributes = DNS_DBATTR_CACHE, |
3306 | 0 | .common.references = 1, |
3307 | 0 | .references = 1, |
3308 | 0 | .buckets_count = nloops, |
3309 | 0 | }; |
3310 | | |
3311 | | /* |
3312 | | * If argv[0] exists, it points to a memory context to use for heap |
3313 | | */ |
3314 | 0 | if (argc != 0) { |
3315 | 0 | hmctx = (isc_mem_t *)argv[0]; |
3316 | 0 | } |
3317 | |
|
3318 | 0 | isc_rwlock_init(&qpdb->lock); |
3319 | 0 | TREE_INITLOCK(&qpdb->tree_lock); |
3320 | |
|
3321 | 0 | qpdb->buckets_count = isc_loopmgr_nloops(); |
3322 | |
|
3323 | 0 | dns_rdatasetstats_create(mctx, &qpdb->rrsetstats); |
3324 | 0 | for (i = 0; i < (int)qpdb->buckets_count; i++) { |
3325 | 0 | ISC_SIEVE_INIT(qpdb->buckets[i].sieve); |
3326 | |
|
3327 | 0 | qpdb->buckets[i].heap = NULL; |
3328 | 0 | isc_heap_create(hmctx, ttl_sooner, set_index, 0, |
3329 | 0 | &qpdb->buckets[i].heap); |
3330 | |
|
3331 | 0 | isc_queue_init(&qpdb->buckets[i].deadnodes); |
3332 | |
|
3333 | 0 | NODE_INITLOCK(&qpdb->buckets[i].lock); |
3334 | 0 | } |
3335 | | |
3336 | | /* |
3337 | | * Attach to the mctx. The database will persist so long as there |
3338 | | * are references to it, and attaching to the mctx ensures that our |
3339 | | * mctx won't disappear out from under us. |
3340 | | */ |
3341 | 0 | isc_mem_attach(mctx, &qpdb->common.mctx); |
3342 | 0 | isc_mem_attach(hmctx, &qpdb->hmctx); |
3343 | | |
3344 | | /* |
3345 | | * Make a copy of the origin name. |
3346 | | */ |
3347 | 0 | dns_name_dup(origin, mctx, &qpdb->common.origin); |
3348 | | |
3349 | | /* |
3350 | | * Make the qp trie. |
3351 | | */ |
3352 | 0 | dns_qp_create(mctx, &qpmethods, qpdb, &qpdb->tree); |
3353 | |
|
3354 | 0 | qpdb->common.magic = DNS_DB_MAGIC; |
3355 | 0 | qpdb->common.impmagic = QPDB_MAGIC; |
3356 | |
|
3357 | 0 | *dbp = (dns_db_t *)qpdb; |
3358 | |
|
3359 | 0 | return ISC_R_SUCCESS; |
3360 | 0 | } |
3361 | | |
3362 | | /* |
3363 | | * Rdataset Iterator Methods |
3364 | | */ |
3365 | | |
3366 | | static void |
3367 | 0 | rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) { |
3368 | 0 | qpc_rditer_t *iterator = NULL; |
3369 | |
|
3370 | 0 | iterator = (qpc_rditer_t *)(*iteratorp); |
3371 | |
|
3372 | 0 | dns__db_detachnode(&iterator->common.node DNS__DB_FLARG_PASS); |
3373 | 0 | isc_mem_put(iterator->common.db->mctx, iterator, sizeof(*iterator)); |
3374 | |
|
3375 | 0 | *iteratorp = NULL; |
3376 | 0 | } |
3377 | | |
3378 | | static bool |
3379 | | iterator_active(qpcache_t *qpdb, qpc_rditer_t *iterator, |
3380 | 0 | dns_slabheader_t *header) { |
3381 | 0 | dns_ttl_t stale_ttl = header->expire + STALE_TTL(header, qpdb); |
3382 | | |
3383 | | /* |
3384 | | * If this header is still active then return it. |
3385 | | */ |
3386 | 0 | if (ACTIVE(header, iterator->common.now)) { |
3387 | 0 | return true; |
3388 | 0 | } |
3389 | | |
3390 | | /* |
3391 | | * If we are not returning stale records or the rdataset is |
3392 | | * too old don't return it. |
3393 | | */ |
3394 | 0 | if (!STALEOK(iterator) || (iterator->common.now > stale_ttl)) { |
3395 | 0 | return false; |
3396 | 0 | } |
3397 | 0 | return true; |
3398 | 0 | } |
3399 | | |
3400 | | static isc_result_t |
3401 | 0 | rdatasetiter_first(dns_rdatasetiter_t *it DNS__DB_FLARG) { |
3402 | 0 | qpc_rditer_t *iterator = (qpc_rditer_t *)it; |
3403 | 0 | qpcache_t *qpdb = (qpcache_t *)(iterator->common.db); |
3404 | 0 | qpcnode_t *qpnode = (qpcnode_t *)iterator->common.node; |
3405 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
3406 | 0 | isc_rwlock_t *nlock = &qpdb->buckets[qpnode->locknum].lock; |
3407 | |
|
3408 | 0 | iterator->current = NULL; |
3409 | |
|
3410 | 0 | NODE_RDLOCK(nlock, &nlocktype); |
3411 | |
|
3412 | 0 | DNS_SLABTOP_FOREACH(top, qpnode->data) { |
3413 | 0 | dns_slabheader_t *header = first_existing_header(top); |
3414 | |
|
3415 | 0 | if (EXPIREDOK(iterator) || |
3416 | 0 | (header != NULL && iterator_active(qpdb, iterator, header))) |
3417 | 0 | { |
3418 | 0 | iterator->current = top; |
3419 | 0 | break; |
3420 | 0 | } |
3421 | 0 | } |
3422 | |
|
3423 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
3424 | |
|
3425 | 0 | if (iterator->current == NULL) { |
3426 | 0 | return ISC_R_NOMORE; |
3427 | 0 | } |
3428 | | |
3429 | 0 | return ISC_R_SUCCESS; |
3430 | 0 | } |
3431 | | |
3432 | | static isc_result_t |
3433 | 0 | rdatasetiter_next(dns_rdatasetiter_t *it DNS__DB_FLARG) { |
3434 | 0 | qpc_rditer_t *iterator = (qpc_rditer_t *)it; |
3435 | 0 | qpcache_t *qpdb = (qpcache_t *)(iterator->common.db); |
3436 | 0 | qpcnode_t *qpnode = (qpcnode_t *)iterator->common.node; |
3437 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
3438 | 0 | isc_rwlock_t *nlock = &qpdb->buckets[qpnode->locknum].lock; |
3439 | 0 | dns_slabtop_t *from = NULL; |
3440 | |
|
3441 | 0 | if (iterator->current == NULL) { |
3442 | 0 | return ISC_R_NOMORE; |
3443 | 0 | } |
3444 | | |
3445 | 0 | NODE_RDLOCK(nlock, &nlocktype); |
3446 | |
|
3447 | 0 | from = cds_list_entry(iterator->current->types_link.next, dns_slabtop_t, |
3448 | 0 | types_link); |
3449 | 0 | iterator->current = NULL; |
3450 | |
|
3451 | 0 | if (from != NULL) { |
3452 | 0 | DNS_SLABTOP_FOREACH_FROM(top, qpnode->data, from) { |
3453 | 0 | dns_slabheader_t *header = first_existing_header(top); |
3454 | |
|
3455 | 0 | if (EXPIREDOK(iterator) || |
3456 | 0 | (header != NULL && |
3457 | 0 | iterator_active(qpdb, iterator, header))) |
3458 | 0 | { |
3459 | 0 | iterator->current = top; |
3460 | 0 | break; |
3461 | 0 | } |
3462 | 0 | } |
3463 | 0 | } |
3464 | |
|
3465 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
3466 | |
|
3467 | 0 | if (iterator->current == NULL) { |
3468 | 0 | return ISC_R_NOMORE; |
3469 | 0 | } |
3470 | | |
3471 | 0 | return ISC_R_SUCCESS; |
3472 | 0 | } |
3473 | | |
3474 | | static void |
3475 | | rdatasetiter_current(dns_rdatasetiter_t *it, |
3476 | 0 | dns_rdataset_t *rdataset DNS__DB_FLARG) { |
3477 | 0 | qpc_rditer_t *iterator = (qpc_rditer_t *)it; |
3478 | 0 | qpcache_t *qpdb = (qpcache_t *)(iterator->common.db); |
3479 | 0 | qpcnode_t *qpnode = (qpcnode_t *)iterator->common.node; |
3480 | 0 | dns_slabtop_t *top = NULL; |
3481 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
3482 | 0 | isc_rwlock_t *nlock = &qpdb->buckets[qpnode->locknum].lock; |
3483 | |
|
3484 | 0 | top = iterator->current; |
3485 | 0 | REQUIRE(top != NULL); |
3486 | |
|
3487 | 0 | NODE_RDLOCK(nlock, &nlocktype); |
3488 | |
|
3489 | 0 | dns_slabheader_t *header = first_existing_header(top); |
3490 | 0 | INSIST(header != NULL); |
3491 | |
|
3492 | 0 | bindrdataset(qpdb, qpnode, header, iterator->common.now, nlocktype, |
3493 | 0 | isc_rwlocktype_none, rdataset DNS__DB_FLARG_PASS); |
3494 | |
|
3495 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
3496 | 0 | } |
3497 | | |
3498 | | /* |
3499 | | * Database Iterator Methods |
3500 | | */ |
3501 | | |
3502 | | static void |
3503 | 0 | reference_iter_node(qpc_dbit_t *qpdbiter DNS__DB_FLARG) { |
3504 | 0 | qpcache_t *qpdb = (qpcache_t *)qpdbiter->common.db; |
3505 | 0 | qpcnode_t *node = qpdbiter->node; |
3506 | |
|
3507 | 0 | if (node == NULL) { |
3508 | 0 | return; |
3509 | 0 | } |
3510 | | |
3511 | 0 | INSIST(qpdbiter->tree_locked != isc_rwlocktype_none); |
3512 | 0 | reactivate_node(qpdb, node, qpdbiter->tree_locked DNS__DB_FLARG_PASS); |
3513 | 0 | } |
3514 | | |
3515 | | static void |
3516 | 0 | dereference_iter_node(qpc_dbit_t *qpdbiter DNS__DB_FLARG) { |
3517 | 0 | qpcache_t *qpdb = (qpcache_t *)qpdbiter->common.db; |
3518 | 0 | qpcnode_t *node = qpdbiter->node; |
3519 | 0 | isc_rwlock_t *nlock = NULL; |
3520 | 0 | isc_rwlocktype_t nlocktype = isc_rwlocktype_none; |
3521 | 0 | isc_rwlocktype_t tlocktype = qpdbiter->tree_locked; |
3522 | |
|
3523 | 0 | if (node == NULL) { |
3524 | 0 | return; |
3525 | 0 | } |
3526 | | |
3527 | 0 | REQUIRE(tlocktype != isc_rwlocktype_write); |
3528 | |
|
3529 | 0 | nlock = &qpdb->buckets[node->locknum].lock; |
3530 | 0 | NODE_RDLOCK(nlock, &nlocktype); |
3531 | 0 | qpcnode_release(qpdb, node, &nlocktype, |
3532 | 0 | &qpdbiter->tree_locked DNS__DB_FLARG_PASS); |
3533 | 0 | NODE_UNLOCK(nlock, &nlocktype); |
3534 | |
|
3535 | 0 | INSIST(qpdbiter->tree_locked == tlocktype); |
3536 | |
|
3537 | 0 | qpdbiter->node = NULL; |
3538 | 0 | } |
3539 | | |
3540 | | static void |
3541 | 0 | resume_iteration(qpc_dbit_t *qpdbiter, bool continuing) { |
3542 | 0 | qpcache_t *qpdb = (qpcache_t *)qpdbiter->common.db; |
3543 | |
|
3544 | 0 | REQUIRE(qpdbiter->paused); |
3545 | 0 | REQUIRE(qpdbiter->tree_locked == isc_rwlocktype_none); |
3546 | |
|
3547 | 0 | TREE_RDLOCK(&qpdb->tree_lock, &qpdbiter->tree_locked); |
3548 | | |
3549 | | /* |
3550 | | * If we're being called from dbiterator_next, we may need |
3551 | | * to reinitialize the iterator to the current name. The |
3552 | | * tree could have changed while it was unlocked, which |
3553 | | * would make the iterator traversal inconsistent. |
3554 | | * |
3555 | | * As long as the iterator is holding a reference to |
3556 | | * qpdbiter->node, the node won't be removed from the tree, |
3557 | | * so the lookup should always succeed. |
3558 | | */ |
3559 | 0 | if (continuing && qpdbiter->node != NULL) { |
3560 | 0 | isc_result_t result; |
3561 | 0 | result = dns_qp_lookup(qpdb->tree, qpdbiter->name, |
3562 | 0 | DNS_DBNAMESPACE_NORMAL, NULL, |
3563 | 0 | &qpdbiter->iter, NULL, NULL, NULL); |
3564 | 0 | INSIST(result == ISC_R_SUCCESS); |
3565 | 0 | } |
3566 | |
|
3567 | 0 | qpdbiter->paused = false; |
3568 | 0 | } |
3569 | | |
3570 | | static void |
3571 | 0 | dbiterator_destroy(dns_dbiterator_t **iteratorp DNS__DB_FLARG) { |
3572 | 0 | qpc_dbit_t *qpdbiter = (qpc_dbit_t *)(*iteratorp); |
3573 | 0 | qpcache_t *qpdb = (qpcache_t *)qpdbiter->common.db; |
3574 | 0 | dns_db_t *db = NULL; |
3575 | |
|
3576 | 0 | if (qpdbiter->tree_locked == isc_rwlocktype_read) { |
3577 | 0 | TREE_UNLOCK(&qpdb->tree_lock, &qpdbiter->tree_locked); |
3578 | 0 | } |
3579 | 0 | INSIST(qpdbiter->tree_locked == isc_rwlocktype_none); |
3580 | |
|
3581 | 0 | dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS); |
3582 | |
|
3583 | 0 | dns_db_attach(qpdbiter->common.db, &db); |
3584 | 0 | dns_db_detach(&qpdbiter->common.db); |
3585 | |
|
3586 | 0 | isc_mem_put(db->mctx, qpdbiter, sizeof(*qpdbiter)); |
3587 | 0 | dns_db_detach(&db); |
3588 | |
|
3589 | 0 | *iteratorp = NULL; |
3590 | 0 | } |
3591 | | |
3592 | | static isc_result_t |
3593 | 0 | dbiterator_first(dns_dbiterator_t *iterator DNS__DB_FLARG) { |
3594 | 0 | isc_result_t result; |
3595 | 0 | qpc_dbit_t *qpdbiter = (qpc_dbit_t *)iterator; |
3596 | 0 | qpcache_t *qpdb = (qpcache_t *)iterator->db; |
3597 | |
|
3598 | 0 | if (qpdbiter->result != ISC_R_SUCCESS && |
3599 | 0 | qpdbiter->result != ISC_R_NOTFOUND && |
3600 | 0 | qpdbiter->result != DNS_R_PARTIALMATCH && |
3601 | 0 | qpdbiter->result != ISC_R_NOMORE) |
3602 | 0 | { |
3603 | 0 | return qpdbiter->result; |
3604 | 0 | } |
3605 | | |
3606 | 0 | if (qpdbiter->paused) { |
3607 | 0 | resume_iteration(qpdbiter, false); |
3608 | 0 | } |
3609 | |
|
3610 | 0 | dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS); |
3611 | |
|
3612 | 0 | dns_qpiter_init(qpdb->tree, &qpdbiter->iter); |
3613 | 0 | result = dns_qpiter_next(&qpdbiter->iter, NULL, |
3614 | 0 | (void **)&qpdbiter->node, NULL); |
3615 | |
|
3616 | 0 | if (result == ISC_R_SUCCESS && |
3617 | 0 | qpdbiter->node->nspace == DNS_DBNAMESPACE_NORMAL) |
3618 | 0 | { |
3619 | 0 | dns_name_copy(&qpdbiter->node->name, qpdbiter->name); |
3620 | 0 | reference_iter_node(qpdbiter DNS__DB_FLARG_PASS); |
3621 | 0 | } else if (result == ISC_R_SUCCESS) { |
3622 | 0 | result = ISC_R_NOMORE; |
3623 | 0 | qpdbiter->node = NULL; |
3624 | 0 | } else { |
3625 | | /* The tree is empty. */ |
3626 | 0 | INSIST(result == ISC_R_NOMORE); |
3627 | 0 | qpdbiter->node = NULL; |
3628 | 0 | } |
3629 | |
|
3630 | 0 | qpdbiter->result = result; |
3631 | |
|
3632 | 0 | if (result != ISC_R_SUCCESS) { |
3633 | 0 | ENSURE(!qpdbiter->paused); |
3634 | 0 | } |
3635 | |
|
3636 | 0 | return result; |
3637 | 0 | } |
3638 | | |
3639 | | static isc_result_t |
3640 | 0 | dbiterator_last(dns_dbiterator_t *iterator ISC_ATTR_UNUSED DNS__DB_FLARG) { |
3641 | 0 | return ISC_R_NOTIMPLEMENTED; |
3642 | 0 | } |
3643 | | |
3644 | | static isc_result_t |
3645 | | dbiterator_seek(dns_dbiterator_t *iterator, |
3646 | 0 | const dns_name_t *name DNS__DB_FLARG) { |
3647 | 0 | isc_result_t result; |
3648 | 0 | qpc_dbit_t *qpdbiter = (qpc_dbit_t *)iterator; |
3649 | 0 | qpcache_t *qpdb = (qpcache_t *)iterator->db; |
3650 | |
|
3651 | 0 | if (qpdbiter->result != ISC_R_SUCCESS && |
3652 | 0 | qpdbiter->result != ISC_R_NOTFOUND && |
3653 | 0 | qpdbiter->result != DNS_R_PARTIALMATCH && |
3654 | 0 | qpdbiter->result != ISC_R_NOMORE) |
3655 | 0 | { |
3656 | 0 | return qpdbiter->result; |
3657 | 0 | } |
3658 | | |
3659 | 0 | if (qpdbiter->paused) { |
3660 | 0 | resume_iteration(qpdbiter, false); |
3661 | 0 | } |
3662 | |
|
3663 | 0 | dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS); |
3664 | |
|
3665 | 0 | result = dns_qp_lookup(qpdb->tree, name, DNS_DBNAMESPACE_NORMAL, NULL, |
3666 | 0 | &qpdbiter->iter, NULL, (void **)&qpdbiter->node, |
3667 | 0 | NULL); |
3668 | |
|
3669 | 0 | if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) { |
3670 | 0 | dns_name_copy(&qpdbiter->node->name, qpdbiter->name); |
3671 | 0 | reference_iter_node(qpdbiter DNS__DB_FLARG_PASS); |
3672 | 0 | } else { |
3673 | 0 | qpdbiter->node = NULL; |
3674 | 0 | } |
3675 | |
|
3676 | 0 | qpdbiter->result = (result == DNS_R_PARTIALMATCH) ? ISC_R_SUCCESS |
3677 | 0 | : result; |
3678 | 0 | return result; |
3679 | 0 | } |
3680 | | |
3681 | | static isc_result_t |
3682 | 0 | dbiterator_prev(dns_dbiterator_t *iterator ISC_ATTR_UNUSED DNS__DB_FLARG) { |
3683 | 0 | return ISC_R_NOTIMPLEMENTED; |
3684 | 0 | } |
3685 | | |
3686 | | static isc_result_t |
3687 | 0 | dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG) { |
3688 | 0 | isc_result_t result; |
3689 | 0 | qpc_dbit_t *qpdbiter = (qpc_dbit_t *)iterator; |
3690 | |
|
3691 | 0 | REQUIRE(qpdbiter->node != NULL); |
3692 | |
|
3693 | 0 | if (qpdbiter->result != ISC_R_SUCCESS) { |
3694 | 0 | return qpdbiter->result; |
3695 | 0 | } |
3696 | | |
3697 | 0 | if (qpdbiter->paused) { |
3698 | 0 | resume_iteration(qpdbiter, true); |
3699 | 0 | } |
3700 | |
|
3701 | 0 | dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS); |
3702 | |
|
3703 | 0 | result = dns_qpiter_next(&qpdbiter->iter, NULL, |
3704 | 0 | (void **)&qpdbiter->node, NULL); |
3705 | |
|
3706 | 0 | if (result == ISC_R_SUCCESS && |
3707 | 0 | qpdbiter->node->nspace == DNS_DBNAMESPACE_NORMAL) |
3708 | 0 | { |
3709 | 0 | dns_name_copy(&qpdbiter->node->name, qpdbiter->name); |
3710 | 0 | reference_iter_node(qpdbiter DNS__DB_FLARG_PASS); |
3711 | 0 | } else if (result == ISC_R_SUCCESS) { |
3712 | 0 | result = ISC_R_NOMORE; |
3713 | 0 | qpdbiter->node = NULL; |
3714 | 0 | } else { |
3715 | 0 | INSIST(result == ISC_R_NOMORE); |
3716 | 0 | qpdbiter->node = NULL; |
3717 | 0 | } |
3718 | |
|
3719 | 0 | qpdbiter->result = result; |
3720 | 0 | return result; |
3721 | 0 | } |
3722 | | |
3723 | | static isc_result_t |
3724 | | dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep, |
3725 | 0 | dns_name_t *name DNS__DB_FLARG) { |
3726 | 0 | qpcache_t *qpdb = (qpcache_t *)iterator->db; |
3727 | 0 | qpc_dbit_t *qpdbiter = (qpc_dbit_t *)iterator; |
3728 | 0 | qpcnode_t *node = qpdbiter->node; |
3729 | |
|
3730 | 0 | REQUIRE(qpdbiter->result == ISC_R_SUCCESS); |
3731 | 0 | REQUIRE(node != NULL); |
3732 | |
|
3733 | 0 | if (qpdbiter->paused) { |
3734 | 0 | resume_iteration(qpdbiter, false); |
3735 | 0 | } |
3736 | |
|
3737 | 0 | if (name != NULL) { |
3738 | 0 | dns_name_copy(&node->name, name); |
3739 | 0 | } |
3740 | |
|
3741 | 0 | qpcnode_acquire(qpdb, node, isc_rwlocktype_none, |
3742 | 0 | qpdbiter->tree_locked DNS__DB_FLARG_PASS); |
3743 | |
|
3744 | 0 | *nodep = (dns_dbnode_t *)qpdbiter->node; |
3745 | 0 | return ISC_R_SUCCESS; |
3746 | 0 | } |
3747 | | |
3748 | | static isc_result_t |
3749 | 0 | dbiterator_pause(dns_dbiterator_t *iterator) { |
3750 | 0 | qpcache_t *qpdb = (qpcache_t *)iterator->db; |
3751 | 0 | qpc_dbit_t *qpdbiter = (qpc_dbit_t *)iterator; |
3752 | |
|
3753 | 0 | if (qpdbiter->result != ISC_R_SUCCESS && |
3754 | 0 | qpdbiter->result != ISC_R_NOTFOUND && |
3755 | 0 | qpdbiter->result != DNS_R_PARTIALMATCH && |
3756 | 0 | qpdbiter->result != ISC_R_NOMORE) |
3757 | 0 | { |
3758 | 0 | return qpdbiter->result; |
3759 | 0 | } |
3760 | | |
3761 | 0 | if (qpdbiter->paused) { |
3762 | 0 | return ISC_R_SUCCESS; |
3763 | 0 | } |
3764 | | |
3765 | 0 | qpdbiter->paused = true; |
3766 | |
|
3767 | 0 | if (qpdbiter->tree_locked == isc_rwlocktype_read) { |
3768 | 0 | TREE_UNLOCK(&qpdb->tree_lock, &qpdbiter->tree_locked); |
3769 | 0 | } |
3770 | 0 | INSIST(qpdbiter->tree_locked == isc_rwlocktype_none); |
3771 | |
|
3772 | 0 | return ISC_R_SUCCESS; |
3773 | 0 | } |
3774 | | |
3775 | | static isc_result_t |
3776 | 0 | dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) { |
3777 | 0 | qpc_dbit_t *qpdbiter = (qpc_dbit_t *)iterator; |
3778 | |
|
3779 | 0 | if (qpdbiter->result != ISC_R_SUCCESS) { |
3780 | 0 | return qpdbiter->result; |
3781 | 0 | } |
3782 | | |
3783 | 0 | dns_name_copy(dns_rootname, name); |
3784 | 0 | return ISC_R_SUCCESS; |
3785 | 0 | } |
3786 | | |
3787 | | static void |
3788 | 0 | qpcnode_deletedata(dns_dbnode_t *node ISC_ATTR_UNUSED, void *data) { |
3789 | 0 | dns_slabheader_t *header = data; |
3790 | 0 | qpcache_t *qpdb = HEADERNODE(header)->qpdb; |
3791 | |
|
3792 | 0 | if (header->heap != NULL && header->heap_index != 0) { |
3793 | 0 | isc_heap_delete(header->heap, header->heap_index); |
3794 | 0 | } |
3795 | | |
3796 | | /* |
3797 | | * This place is the only place where we actually need header->typepair. |
3798 | | */ |
3799 | 0 | update_rrsetstats(qpdb->rrsetstats, header->typepair, |
3800 | 0 | atomic_load_acquire(&header->attributes), false); |
3801 | |
|
3802 | 0 | if (header->noqname != NULL) { |
3803 | 0 | dns_slabheader_freeproof(qpdb->common.mctx, &header->noqname); |
3804 | 0 | } |
3805 | 0 | if (header->closest != NULL) { |
3806 | 0 | dns_slabheader_freeproof(qpdb->common.mctx, &header->closest); |
3807 | 0 | } |
3808 | 0 | } |
3809 | | |
3810 | | /* |
3811 | | * Caller must be holding the node write lock. |
3812 | | */ |
3813 | | static void |
3814 | | expire_ttl_headers(qpcache_t *qpdb, unsigned int locknum, |
3815 | | isc_rwlocktype_t *nlocktypep, isc_rwlocktype_t *tlocktypep, |
3816 | 0 | isc_stdtime_t now DNS__DB_FLARG) { |
3817 | 0 | isc_heap_t *heap = qpdb->buckets[locknum].heap; |
3818 | |
|
3819 | 0 | for (size_t i = 0; i < DNS_QPDB_EXPIRE_TTL_COUNT; i++) { |
3820 | 0 | dns_slabheader_t *header = isc_heap_element(heap, 1); |
3821 | |
|
3822 | 0 | if (header == NULL) { |
3823 | | /* No headers left on this TTL heap; exit cleaning */ |
3824 | 0 | return; |
3825 | 0 | } |
3826 | | |
3827 | 0 | dns_ttl_t ttl = header->expire + STALE_TTL(header, qpdb); |
3828 | |
|
3829 | 0 | if (ttl >= now - QPDB_VIRTUAL) { |
3830 | | /* |
3831 | | * The header at the top of this TTL heap is not yet |
3832 | | * eligible for expiry, so none of the other headers on |
3833 | | * the same heap can be eligible for expiry, either; |
3834 | | * exit cleaning. |
3835 | | */ |
3836 | 0 | return; |
3837 | 0 | } |
3838 | | |
3839 | 0 | (void)expireheader(header, nlocktypep, tlocktypep, |
3840 | 0 | dns_expire_ttl DNS__DB_FLARG_PASS); |
3841 | 0 | } |
3842 | 0 | } |
3843 | | |
3844 | | static void |
3845 | 0 | setmaxrrperset(dns_db_t *db, uint32_t value) { |
3846 | 0 | qpcache_t *qpdb = (qpcache_t *)db; |
3847 | |
|
3848 | 0 | REQUIRE(VALID_QPDB(qpdb)); |
3849 | |
|
3850 | 0 | qpdb->maxrrperset = value; |
3851 | 0 | } |
3852 | | |
3853 | | static void |
3854 | 0 | setmaxtypepername(dns_db_t *db, uint32_t value) { |
3855 | 0 | qpcache_t *qpdb = (qpcache_t *)db; |
3856 | |
|
3857 | 0 | REQUIRE(VALID_QPDB(qpdb)); |
3858 | |
|
3859 | 0 | qpdb->maxtypepername = value; |
3860 | 0 | } |
3861 | | |
3862 | | static dns_dbmethods_t qpdb_cachemethods = { |
3863 | | .destroy = qpcache_destroy, |
3864 | | .findnode = qpcache_findnode, |
3865 | | .find = qpcache_find, |
3866 | | .findzonecut = qpcache_findzonecut, |
3867 | | .createiterator = qpcache_createiterator, |
3868 | | .findrdataset = qpcache_findrdataset, |
3869 | | .allrdatasets = qpcache_allrdatasets, |
3870 | | .addrdataset = qpcache_addrdataset, |
3871 | | .deleterdataset = qpcache_deleterdataset, |
3872 | | .nodecount = nodecount, |
3873 | | .getrrsetstats = getrrsetstats, |
3874 | | .setcachestats = setcachestats, |
3875 | | .setservestalettl = setservestalettl, |
3876 | | .getservestalettl = getservestalettl, |
3877 | | .setservestalerefresh = setservestalerefresh, |
3878 | | .getservestalerefresh = getservestalerefresh, |
3879 | | .setmaxrrperset = setmaxrrperset, |
3880 | | .setmaxtypepername = setmaxtypepername, |
3881 | | }; |
3882 | | |
3883 | | static void |
3884 | 0 | qpcnode_destroy(qpcnode_t *qpnode) { |
3885 | 0 | qpcache_t *qpdb = qpnode->qpdb; |
3886 | |
|
3887 | 0 | DNS_SLABTOP_FOREACH(top, qpnode->data) { |
3888 | 0 | dns_slabheader_t *header = NULL, *header_next = NULL; |
3889 | 0 | cds_list_for_each_entry_safe(header, header_next, &top->headers, |
3890 | 0 | headers_link) |
3891 | 0 | { |
3892 | 0 | cds_list_del(&header->headers_link); |
3893 | 0 | dns_slabheader_destroy(&header); |
3894 | 0 | } |
3895 | |
|
3896 | 0 | if (ISC_SIEVE_LINKED(top, link)) { |
3897 | 0 | ISC_SIEVE_UNLINK(qpdb->buckets[qpnode->locknum].sieve, |
3898 | 0 | top, link); |
3899 | 0 | } |
3900 | 0 | dns_slabtop_destroy(((dns_db_t *)qpdb)->mctx, &top); |
3901 | 0 | } |
3902 | |
|
3903 | 0 | dns_name_free(&qpnode->name, qpnode->mctx); |
3904 | 0 | isc_mem_putanddetach(&qpnode->mctx, qpnode, sizeof(qpcnode_t)); |
3905 | 0 | } |
3906 | | |
3907 | | #ifdef DNS_DB_NODETRACE |
3908 | | ISC_REFCOUNT_STATIC_TRACE_IMPL(qpcnode, qpcnode_destroy); |
3909 | | #else |
3910 | 0 | ISC_REFCOUNT_STATIC_IMPL(qpcnode, qpcnode_destroy); Unexecuted instantiation: qpcache.c:qpcnode_ref Unexecuted instantiation: qpcache.c:qpcnode_detach Unexecuted instantiation: qpcache.c:qpcnode_unref |
3911 | 0 | #endif |
3912 | 0 |
|
3913 | 0 | #ifdef DNS_DB_NODETRACE |
3914 | 0 | ISC_REFCOUNT_STATIC_TRACE_IMPL(qpcache, qpcache__destroy); |
3915 | 0 | #else |
3916 | | ISC_REFCOUNT_STATIC_IMPL(qpcache, qpcache__destroy); Unexecuted instantiation: qpcache.c:qpcache_detach Unexecuted instantiation: qpcache.c:qpcache_unref Unexecuted instantiation: qpcache.c:qpcache_ref |
3917 | | #endif |