rbtdb.c revision 1.3 1 1.2 christos /* $NetBSD: rbtdb.c,v 1.3 2019/01/09 16:55:11 christos Exp $ */
2 1.1 christos
3 1.1 christos /*
4 1.1 christos * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 1.1 christos *
6 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public
7 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this
8 1.1 christos * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 1.1 christos *
10 1.1 christos * See the COPYRIGHT file distributed with this work for additional
11 1.1 christos * information regarding copyright ownership.
12 1.1 christos */
13 1.1 christos
14 1.1 christos /*! \file */
15 1.1 christos
16 1.1 christos #include <config.h>
17 1.1 christos
18 1.1 christos /* #define inline */
19 1.1 christos
20 1.3 christos #include <inttypes.h>
21 1.3 christos #include <stdbool.h>
22 1.1 christos
23 1.1 christos #include <isc/crc64.h>
24 1.1 christos #include <isc/event.h>
25 1.1 christos #include <isc/heap.h>
26 1.1 christos #include <isc/file.h>
27 1.1 christos #include <isc/hex.h>
28 1.1 christos #include <isc/mem.h>
29 1.1 christos #include <isc/mutex.h>
30 1.1 christos #include <isc/once.h>
31 1.1 christos #include <isc/platform.h>
32 1.1 christos #include <isc/print.h>
33 1.1 christos #include <isc/random.h>
34 1.1 christos #include <isc/refcount.h>
35 1.1 christos #include <isc/rwlock.h>
36 1.1 christos #include <isc/serial.h>
37 1.1 christos #include <isc/socket.h>
38 1.1 christos #include <isc/stdio.h>
39 1.1 christos #include <isc/string.h>
40 1.1 christos #include <isc/task.h>
41 1.1 christos #include <isc/time.h>
42 1.1 christos #include <isc/util.h>
43 1.1 christos #include <isc/hash.h>
44 1.1 christos
45 1.1 christos #include <dns/callbacks.h>
46 1.1 christos #include <dns/db.h>
47 1.1 christos #include <dns/dbiterator.h>
48 1.1 christos #include <dns/events.h>
49 1.1 christos #include <dns/fixedname.h>
50 1.1 christos #include <dns/lib.h>
51 1.1 christos #include <dns/log.h>
52 1.1 christos #include <dns/masterdump.h>
53 1.1 christos #include <dns/nsec.h>
54 1.1 christos #include <dns/nsec3.h>
55 1.1 christos #include <dns/rbt.h>
56 1.1 christos #include <dns/rdata.h>
57 1.1 christos #include <dns/rdataset.h>
58 1.1 christos #include <dns/rdatasetiter.h>
59 1.1 christos #include <dns/rdataslab.h>
60 1.1 christos #include <dns/rdatastruct.h>
61 1.1 christos #include <dns/result.h>
62 1.1 christos #include <dns/stats.h>
63 1.1 christos #include <dns/time.h>
64 1.1 christos #include <dns/version.h>
65 1.1 christos #include <dns/view.h>
66 1.1 christos #include <dns/zone.h>
67 1.1 christos #include <dns/zonekey.h>
68 1.1 christos
69 1.1 christos #ifndef WIN32
70 1.1 christos #include <sys/mman.h>
71 1.1 christos #else
72 1.1 christos #define PROT_READ 0x01
73 1.1 christos #define PROT_WRITE 0x02
74 1.1 christos #define MAP_PRIVATE 0x0002
75 1.1 christos #define MAP_FAILED ((void *)-1)
76 1.1 christos #endif
77 1.1 christos
78 1.1 christos #include "rbtdb.h"
79 1.1 christos
80 1.1 christos #define RBTDB_MAGIC ISC_MAGIC('R', 'B', 'D', '4')
81 1.1 christos
82 1.1 christos #define CHECK(op) \
83 1.1 christos do { result = (op); \
84 1.1 christos if (result != ISC_R_SUCCESS) goto failure; \
85 1.2 christos } while (/*CONSTCOND*/0)
86 1.1 christos
87 1.1 christos /*
88 1.1 christos * This is the map file header for RBTDB images. It is populated, and then
89 1.1 christos * written, as the LAST thing done to the file. Writing this last (with
90 1.1 christos * zeros in the header area initially) will ensure that the header is only
91 1.1 christos * valid when the RBTDB image is also valid.
92 1.1 christos */
93 1.1 christos typedef struct rbtdb_file_header rbtdb_file_header_t;
94 1.1 christos
95 1.1 christos /* Header length, always the same size regardless of structure size */
96 1.1 christos #define RBTDB_HEADER_LENGTH 1024
97 1.1 christos
98 1.1 christos struct rbtdb_file_header {
99 1.1 christos char version1[32];
100 1.3 christos uint32_t ptrsize;
101 1.1 christos unsigned int bigendian:1;
102 1.3 christos uint64_t tree;
103 1.3 christos uint64_t nsec;
104 1.3 christos uint64_t nsec3;
105 1.1 christos
106 1.1 christos char version2[32]; /* repeated; must match version1 */
107 1.1 christos };
108 1.1 christos
109 1.1 christos
110 1.1 christos /*%
111 1.1 christos * Note that "impmagic" is not the first four bytes of the struct, so
112 1.1 christos * ISC_MAGIC_VALID cannot be used.
113 1.1 christos */
114 1.1 christos #define VALID_RBTDB(rbtdb) ((rbtdb) != NULL && \
115 1.1 christos (rbtdb)->common.impmagic == RBTDB_MAGIC)
116 1.1 christos
117 1.3 christos typedef uint32_t rbtdb_serial_t;
118 1.3 christos typedef uint32_t rbtdb_rdatatype_t;
119 1.1 christos
120 1.1 christos #define RBTDB_RDATATYPE_BASE(type) ((dns_rdatatype_t)((type) & 0xFFFF))
121 1.1 christos #define RBTDB_RDATATYPE_EXT(type) ((dns_rdatatype_t)((type) >> 16))
122 1.3 christos #define RBTDB_RDATATYPE_VALUE(base, ext) ((rbtdb_rdatatype_t)(((uint32_t)ext) << 16) | (((uint32_t)base) & 0xffff))
123 1.1 christos
124 1.1 christos #define RBTDB_RDATATYPE_SIGNSEC \
125 1.1 christos RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_nsec)
126 1.1 christos #define RBTDB_RDATATYPE_SIGNSEC3 \
127 1.1 christos RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_nsec3)
128 1.1 christos #define RBTDB_RDATATYPE_SIGNS \
129 1.1 christos RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_ns)
130 1.1 christos #define RBTDB_RDATATYPE_SIGCNAME \
131 1.1 christos RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_cname)
132 1.1 christos #define RBTDB_RDATATYPE_SIGDNAME \
133 1.1 christos RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_dname)
134 1.1 christos #define RBTDB_RDATATYPE_SIGDDS \
135 1.1 christos RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_ds)
136 1.1 christos #define RBTDB_RDATATYPE_NCACHEANY \
137 1.1 christos RBTDB_RDATATYPE_VALUE(0, dns_rdatatype_any)
138 1.1 christos
139 1.1 christos #define RBTDB_INITLOCK(l) isc_rwlock_init((l), 0, 0)
140 1.1 christos #define RBTDB_DESTROYLOCK(l) isc_rwlock_destroy(l)
141 1.1 christos #define RBTDB_LOCK(l, t) RWLOCK((l), (t))
142 1.1 christos #define RBTDB_UNLOCK(l, t) RWUNLOCK((l), (t))
143 1.1 christos
144 1.1 christos /*
145 1.1 christos * Since node locking is sensitive to both performance and memory footprint,
146 1.1 christos * we need some trick here. If we have both high-performance rwlock and
147 1.1 christos * high performance and small-memory reference counters, we use rwlock for
148 1.1 christos * node lock and isc_refcount for node references. In this case, we don't have
149 1.1 christos * to protect the access to the counters by locks.
150 1.1 christos * Otherwise, we simply use ordinary mutex lock for node locking, and use
151 1.1 christos * simple integers as reference counters which is protected by the lock.
152 1.1 christos * In most cases, we can simply use wrapper macros such as NODE_LOCK and
153 1.1 christos * NODE_UNLOCK. In some other cases, however, we need to protect reference
154 1.1 christos * counters first and then protect other parts of a node as read-only data.
155 1.1 christos * Special additional macros, NODE_STRONGLOCK(), NODE_WEAKLOCK(), etc, are also
156 1.1 christos * provided for these special cases. When we can use the efficient backend
157 1.1 christos * routines, we should only protect the "other members" by NODE_WEAKLOCK(read).
158 1.1 christos * Otherwise, we should use NODE_STRONGLOCK() to protect the entire critical
159 1.1 christos * section including the access to the reference counter.
160 1.1 christos * Note that we cannot use NODE_LOCK()/NODE_UNLOCK() wherever the protected
161 1.1 christos * section is also protected by NODE_STRONGLOCK().
162 1.1 christos */
163 1.1 christos typedef isc_rwlock_t nodelock_t;
164 1.1 christos
165 1.1 christos #define NODE_INITLOCK(l) isc_rwlock_init((l), 0, 0)
166 1.1 christos #define NODE_DESTROYLOCK(l) isc_rwlock_destroy(l)
167 1.1 christos #define NODE_LOCK(l, t) RWLOCK((l), (t))
168 1.1 christos #define NODE_UNLOCK(l, t) RWUNLOCK((l), (t))
169 1.1 christos #define NODE_TRYUPGRADE(l) isc_rwlock_tryupgrade(l)
170 1.3 christos #define NODE_DOWNGRADE(l) isc_rwlock_downgrade(l)
171 1.1 christos
172 1.1 christos /*%
173 1.1 christos * Whether to rate-limit updating the LRU to avoid possible thread contention.
174 1.1 christos * Our performance measurement has shown the cost is marginal, so it's defined
175 1.1 christos * to be 0 by default either with or without threads.
176 1.1 christos */
177 1.1 christos #ifndef DNS_RBTDB_LIMITLRUUPDATE
178 1.1 christos #define DNS_RBTDB_LIMITLRUUPDATE 0
179 1.1 christos #endif
180 1.1 christos
181 1.1 christos /*
182 1.1 christos * Allow clients with a virtual time of up to 5 minutes in the past to see
183 1.1 christos * records that would have otherwise have expired.
184 1.1 christos */
185 1.1 christos #define RBTDB_VIRTUAL 300
186 1.1 christos
187 1.1 christos struct noqname {
188 1.1 christos dns_name_t name;
189 1.1 christos void * neg;
190 1.1 christos void * negsig;
191 1.1 christos dns_rdatatype_t type;
192 1.1 christos };
193 1.1 christos
194 1.1 christos typedef struct rdatasetheader {
195 1.1 christos /*%
196 1.1 christos * Locked by the owning node's lock.
197 1.1 christos */
198 1.1 christos rbtdb_serial_t serial;
199 1.1 christos dns_ttl_t rdh_ttl;
200 1.1 christos rbtdb_rdatatype_t type;
201 1.3 christos uint16_t attributes;
202 1.1 christos dns_trust_t trust;
203 1.1 christos struct noqname *noqname;
204 1.1 christos struct noqname *closest;
205 1.1 christos unsigned int is_mmapped : 1;
206 1.1 christos unsigned int next_is_relative : 1;
207 1.1 christos unsigned int node_is_relative : 1;
208 1.1 christos unsigned int resign_lsb : 1;
209 1.1 christos /*%<
210 1.1 christos * We don't use the LIST macros, because the LIST structure has
211 1.1 christos * both head and tail pointers, and is doubly linked.
212 1.1 christos */
213 1.1 christos
214 1.1 christos struct rdatasetheader *next;
215 1.1 christos /*%<
216 1.1 christos * If this is the top header for an rdataset, 'next' points
217 1.1 christos * to the top header for the next rdataset (i.e., the next type).
218 1.1 christos * Otherwise, it points up to the header whose down pointer points
219 1.1 christos * at this header.
220 1.1 christos */
221 1.1 christos
222 1.1 christos struct rdatasetheader *down;
223 1.1 christos /*%<
224 1.1 christos * Points to the header for the next older version of
225 1.1 christos * this rdataset.
226 1.1 christos */
227 1.1 christos
228 1.3 christos uint32_t count;
229 1.1 christos /*%<
230 1.1 christos * Monotonously increased every time this rdataset is bound so that
231 1.1 christos * it is used as the base of the starting point in DNS responses
232 1.1 christos * when the "cyclic" rrset-order is required. Since the ordering
233 1.1 christos * should not be so crucial, no lock is set for the counter for
234 1.1 christos * performance reasons.
235 1.1 christos */
236 1.1 christos
237 1.1 christos dns_rbtnode_t *node;
238 1.1 christos isc_stdtime_t last_used;
239 1.1 christos ISC_LINK(struct rdatasetheader) link;
240 1.1 christos
241 1.1 christos unsigned int heap_index;
242 1.1 christos /*%<
243 1.1 christos * Used for TTL-based cache cleaning.
244 1.1 christos */
245 1.1 christos isc_stdtime_t resign;
246 1.1 christos /*%<
247 1.1 christos * Case vector. If the bit is set then the corresponding
248 1.1 christos * character in the owner name needs to be AND'd with 0x20,
249 1.1 christos * rendering that character upper case.
250 1.1 christos */
251 1.1 christos unsigned char upper[32];
252 1.1 christos } rdatasetheader_t;
253 1.1 christos
254 1.1 christos typedef ISC_LIST(rdatasetheader_t) rdatasetheaderlist_t;
255 1.1 christos typedef ISC_LIST(dns_rbtnode_t) rbtnodelist_t;
256 1.1 christos
257 1.1 christos #define RDATASET_ATTR_NONEXISTENT 0x0001
258 1.1 christos /*%< May be potentially served as stale data. */
259 1.1 christos #define RDATASET_ATTR_STALE 0x0002
260 1.1 christos #define RDATASET_ATTR_IGNORE 0x0004
261 1.1 christos #define RDATASET_ATTR_RETAIN 0x0008
262 1.1 christos #define RDATASET_ATTR_NXDOMAIN 0x0010
263 1.1 christos #define RDATASET_ATTR_RESIGN 0x0020
264 1.1 christos #define RDATASET_ATTR_STATCOUNT 0x0040
265 1.1 christos #define RDATASET_ATTR_OPTOUT 0x0080
266 1.1 christos #define RDATASET_ATTR_NEGATIVE 0x0100
267 1.1 christos #define RDATASET_ATTR_PREFETCH 0x0200
268 1.1 christos #define RDATASET_ATTR_CASESET 0x0400
269 1.1 christos #define RDATASET_ATTR_ZEROTTL 0x0800
270 1.1 christos #define RDATASET_ATTR_CASEFULLYLOWER 0x1000
271 1.1 christos /*%< Ancient - awaiting cleanup. */
272 1.1 christos #define RDATASET_ATTR_ANCIENT 0x2000
273 1.1 christos
274 1.1 christos /*
275 1.1 christos * XXX
276 1.1 christos * When the cache will pre-expire data (due to memory low or other
277 1.1 christos * situations) before the rdataset's TTL has expired, it MUST
278 1.1 christos * respect the RETAIN bit and not expire the data until its TTL is
279 1.1 christos * expired.
280 1.1 christos */
281 1.1 christos
282 1.1 christos #undef IGNORE /* WIN32 winbase.h defines this. */
283 1.1 christos
284 1.1 christos #define EXISTS(header) \
285 1.1 christos (((header)->attributes & RDATASET_ATTR_NONEXISTENT) == 0)
286 1.1 christos #define NONEXISTENT(header) \
287 1.1 christos (((header)->attributes & RDATASET_ATTR_NONEXISTENT) != 0)
288 1.1 christos #define IGNORE(header) \
289 1.1 christos (((header)->attributes & RDATASET_ATTR_IGNORE) != 0)
290 1.1 christos #define RETAIN(header) \
291 1.1 christos (((header)->attributes & RDATASET_ATTR_RETAIN) != 0)
292 1.1 christos #define NXDOMAIN(header) \
293 1.1 christos (((header)->attributes & RDATASET_ATTR_NXDOMAIN) != 0)
294 1.1 christos #define STALE(header) \
295 1.1 christos (((header)->attributes & RDATASET_ATTR_STALE) != 0)
296 1.1 christos #define RESIGN(header) \
297 1.1 christos (((header)->attributes & RDATASET_ATTR_RESIGN) != 0)
298 1.1 christos #define OPTOUT(header) \
299 1.1 christos (((header)->attributes & RDATASET_ATTR_OPTOUT) != 0)
300 1.1 christos #define NEGATIVE(header) \
301 1.1 christos (((header)->attributes & RDATASET_ATTR_NEGATIVE) != 0)
302 1.1 christos #define PREFETCH(header) \
303 1.1 christos (((header)->attributes & RDATASET_ATTR_PREFETCH) != 0)
304 1.1 christos #define CASESET(header) \
305 1.1 christos (((header)->attributes & RDATASET_ATTR_CASESET) != 0)
306 1.1 christos #define ZEROTTL(header) \
307 1.1 christos (((header)->attributes & RDATASET_ATTR_ZEROTTL) != 0)
308 1.1 christos #define CASEFULLYLOWER(header) \
309 1.1 christos (((header)->attributes & RDATASET_ATTR_CASEFULLYLOWER) != 0)
310 1.1 christos #define ANCIENT(header) \
311 1.1 christos (((header)->attributes & RDATASET_ATTR_ANCIENT) != 0)
312 1.1 christos
313 1.1 christos #define ACTIVE(header, now) \
314 1.1 christos (((header)->rdh_ttl > (now)) || \
315 1.1 christos ((header)->rdh_ttl == (now) && ZEROTTL(header)))
316 1.1 christos
317 1.1 christos #define DEFAULT_NODE_LOCK_COUNT 7 /*%< Should be prime. */
318 1.1 christos #define RBTDB_GLUE_TABLE_INIT_SIZE 2U
319 1.1 christos
320 1.1 christos /*%
321 1.1 christos * Number of buckets for cache DB entries (locks, LRU lists, TTL heaps).
322 1.1 christos * There is a tradeoff issue about configuring this value: if this is too
323 1.1 christos * small, it may cause heavier contention between threads; if this is too large,
324 1.1 christos * LRU purge algorithm won't work well (entries tend to be purged prematurely).
325 1.1 christos * The default value should work well for most environments, but this can
326 1.1 christos * also be configurable at compilation time via the
327 1.1 christos * DNS_RBTDB_CACHE_NODE_LOCK_COUNT variable. This value must be larger than
328 1.1 christos * 1 due to the assumption of overmem_purge().
329 1.1 christos */
330 1.1 christos #ifdef DNS_RBTDB_CACHE_NODE_LOCK_COUNT
331 1.1 christos #if DNS_RBTDB_CACHE_NODE_LOCK_COUNT <= 1
332 1.1 christos #error "DNS_RBTDB_CACHE_NODE_LOCK_COUNT must be larger than 1"
333 1.1 christos #else
334 1.1 christos #define DEFAULT_CACHE_NODE_LOCK_COUNT DNS_RBTDB_CACHE_NODE_LOCK_COUNT
335 1.1 christos #endif
336 1.1 christos #else
337 1.1 christos #define DEFAULT_CACHE_NODE_LOCK_COUNT 16
338 1.1 christos #endif /* DNS_RBTDB_CACHE_NODE_LOCK_COUNT */
339 1.1 christos
340 1.1 christos typedef struct {
341 1.1 christos nodelock_t lock;
342 1.1 christos /* Protected in the refcount routines. */
343 1.1 christos isc_refcount_t references;
344 1.1 christos /* Locked by lock. */
345 1.3 christos bool exiting;
346 1.1 christos } rbtdb_nodelock_t;
347 1.1 christos
348 1.1 christos typedef struct rbtdb_changed {
349 1.1 christos dns_rbtnode_t * node;
350 1.3 christos bool dirty;
351 1.1 christos ISC_LINK(struct rbtdb_changed) link;
352 1.1 christos } rbtdb_changed_t;
353 1.1 christos
354 1.1 christos typedef ISC_LIST(rbtdb_changed_t) rbtdb_changedlist_t;
355 1.1 christos
356 1.1 christos typedef enum {
357 1.1 christos dns_db_insecure,
358 1.1 christos dns_db_partial,
359 1.1 christos dns_db_secure
360 1.1 christos } dns_db_secure_t;
361 1.1 christos
362 1.1 christos typedef struct dns_rbtdb dns_rbtdb_t;
363 1.1 christos
364 1.1 christos /* Reason for expiring a record from cache */
365 1.1 christos typedef enum {
366 1.1 christos expire_lru,
367 1.1 christos expire_ttl,
368 1.1 christos expire_flush
369 1.1 christos } expire_t;
370 1.1 christos
371 1.1 christos typedef struct rbtdb_glue rbtdb_glue_t;
372 1.1 christos
373 1.1 christos typedef struct rbtdb_glue_table_node {
374 1.1 christos struct rbtdb_glue_table_node *next;
375 1.1 christos dns_rbtnode_t *node;
376 1.1 christos rbtdb_glue_t *glue_list;
377 1.1 christos } rbtdb_glue_table_node_t;
378 1.1 christos
379 1.1 christos typedef enum {
380 1.1 christos rdataset_ttl_fresh,
381 1.1 christos rdataset_ttl_stale,
382 1.1 christos rdataset_ttl_ancient
383 1.1 christos } rdataset_ttl_t;
384 1.1 christos
385 1.1 christos typedef struct rbtdb_version {
386 1.1 christos /* Not locked */
387 1.1 christos rbtdb_serial_t serial;
388 1.1 christos dns_rbtdb_t * rbtdb;
389 1.1 christos /*
390 1.1 christos * Protected in the refcount routines.
391 1.1 christos * XXXJT: should we change the lock policy based on the refcount
392 1.1 christos * performance?
393 1.1 christos */
394 1.1 christos isc_refcount_t references;
395 1.1 christos /* Locked by database lock. */
396 1.3 christos bool writer;
397 1.3 christos bool commit_ok;
398 1.1 christos rbtdb_changedlist_t changed_list;
399 1.1 christos rdatasetheaderlist_t resigned_list;
400 1.1 christos ISC_LINK(struct rbtdb_version) link;
401 1.1 christos dns_db_secure_t secure;
402 1.3 christos bool havensec3;
403 1.1 christos /* NSEC3 parameters */
404 1.1 christos dns_hash_t hash;
405 1.3 christos uint8_t flags;
406 1.3 christos uint16_t iterations;
407 1.3 christos uint8_t salt_length;
408 1.1 christos unsigned char salt[DNS_NSEC3_SALTSIZE];
409 1.1 christos
410 1.1 christos /*
411 1.1 christos * records and bytes are covered by rwlock.
412 1.1 christos */
413 1.1 christos isc_rwlock_t rwlock;
414 1.3 christos uint64_t records;
415 1.3 christos uint64_t bytes;
416 1.1 christos
417 1.1 christos isc_rwlock_t glue_rwlock;
418 1.1 christos size_t glue_table_size;
419 1.1 christos size_t glue_table_nodecount;
420 1.1 christos rbtdb_glue_table_node_t **glue_table;
421 1.1 christos } rbtdb_version_t;
422 1.1 christos
423 1.1 christos typedef ISC_LIST(rbtdb_version_t) rbtdb_versionlist_t;
424 1.1 christos
425 1.1 christos struct dns_rbtdb {
426 1.1 christos /* Unlocked. */
427 1.1 christos dns_db_t common;
428 1.1 christos /* Locks the data in this struct */
429 1.1 christos isc_rwlock_t lock;
430 1.1 christos /* Locks the tree structure (prevents nodes appearing/disappearing) */
431 1.1 christos isc_rwlock_t tree_lock;
432 1.1 christos /* Locks for individual tree nodes */
433 1.1 christos unsigned int node_lock_count;
434 1.1 christos rbtdb_nodelock_t * node_locks;
435 1.1 christos dns_rbtnode_t * origin_node;
436 1.1 christos dns_rbtnode_t * nsec3_origin_node;
437 1.1 christos dns_stats_t * rrsetstats; /* cache DB only */
438 1.1 christos isc_stats_t * cachestats; /* cache DB only */
439 1.1 christos isc_stats_t * gluecachestats; /* zone DB only */
440 1.1 christos /* Locked by lock. */
441 1.1 christos unsigned int active;
442 1.1 christos isc_refcount_t references;
443 1.1 christos unsigned int attributes;
444 1.1 christos rbtdb_serial_t current_serial;
445 1.1 christos rbtdb_serial_t least_serial;
446 1.1 christos rbtdb_serial_t next_serial;
447 1.1 christos rbtdb_version_t * current_version;
448 1.1 christos rbtdb_version_t * future_version;
449 1.1 christos rbtdb_versionlist_t open_versions;
450 1.1 christos isc_task_t * task;
451 1.1 christos dns_dbnode_t *soanode;
452 1.1 christos dns_dbnode_t *nsnode;
453 1.1 christos
454 1.1 christos /*
455 1.1 christos * Maximum length of time to keep using a stale answer past its
456 1.1 christos * normal TTL expiry.
457 1.1 christos */
458 1.1 christos dns_ttl_t serve_stale_ttl;
459 1.1 christos
460 1.1 christos /*
461 1.1 christos * This is a linked list used to implement the LRU cache. There will
462 1.1 christos * be node_lock_count linked lists here. Nodes in bucket 1 will be
463 1.1 christos * placed on the linked list rdatasets[1].
464 1.1 christos */
465 1.1 christos rdatasetheaderlist_t *rdatasets;
466 1.1 christos
467 1.1 christos /*%
468 1.1 christos * Temporary storage for stale cache nodes and dynamically deleted
469 1.1 christos * nodes that await being cleaned up.
470 1.1 christos */
471 1.1 christos rbtnodelist_t *deadnodes;
472 1.1 christos
473 1.1 christos /*
474 1.1 christos * Heaps. These are used for TTL based expiry in a cache,
475 1.1 christos * or for zone resigning in a zone DB. hmctx is the memory
476 1.1 christos * context to use for the heap (which differs from the main
477 1.1 christos * database memory context in the case of a cache).
478 1.1 christos */
479 1.1 christos isc_mem_t *hmctx;
480 1.1 christos isc_heap_t **heaps;
481 1.1 christos
482 1.1 christos /*
483 1.1 christos * Base values for the mmap() code.
484 1.1 christos */
485 1.1 christos void * mmap_location;
486 1.1 christos size_t mmap_size;
487 1.1 christos
488 1.1 christos /* Locked by tree_lock. */
489 1.1 christos dns_rbt_t * tree;
490 1.1 christos dns_rbt_t * nsec;
491 1.1 christos dns_rbt_t * nsec3;
492 1.1 christos
493 1.1 christos /* Unlocked */
494 1.1 christos unsigned int quantum;
495 1.1 christos };
496 1.1 christos
497 1.1 christos #define RBTDB_ATTR_LOADED 0x01
498 1.1 christos #define RBTDB_ATTR_LOADING 0x02
499 1.1 christos
500 1.1 christos #define KEEPSTALE(rbtdb) ((rbtdb)->serve_stale_ttl > 0)
501 1.1 christos
502 1.1 christos /*%
503 1.1 christos * Search Context
504 1.1 christos */
505 1.1 christos typedef struct {
506 1.1 christos dns_rbtdb_t * rbtdb;
507 1.1 christos rbtdb_version_t * rbtversion;
508 1.1 christos rbtdb_serial_t serial;
509 1.1 christos unsigned int options;
510 1.1 christos dns_rbtnodechain_t chain;
511 1.3 christos bool copy_name;
512 1.3 christos bool need_cleanup;
513 1.3 christos bool wild;
514 1.1 christos dns_rbtnode_t * zonecut;
515 1.1 christos rdatasetheader_t * zonecut_rdataset;
516 1.1 christos rdatasetheader_t * zonecut_sigrdataset;
517 1.1 christos dns_fixedname_t zonecut_name;
518 1.1 christos isc_stdtime_t now;
519 1.1 christos } rbtdb_search_t;
520 1.1 christos
521 1.1 christos /*%
522 1.1 christos * Load Context
523 1.1 christos */
524 1.1 christos typedef struct {
525 1.1 christos dns_rbtdb_t * rbtdb;
526 1.1 christos isc_stdtime_t now;
527 1.1 christos } rbtdb_load_t;
528 1.1 christos
529 1.1 christos static void delete_callback(void *data, void *arg);
530 1.1 christos static void rdataset_disassociate(dns_rdataset_t *rdataset);
531 1.1 christos static isc_result_t rdataset_first(dns_rdataset_t *rdataset);
532 1.1 christos static isc_result_t rdataset_next(dns_rdataset_t *rdataset);
533 1.1 christos static void rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata);
534 1.1 christos static void rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target);
535 1.1 christos static unsigned int rdataset_count(dns_rdataset_t *rdataset);
536 1.1 christos static isc_result_t rdataset_getnoqname(dns_rdataset_t *rdataset,
537 1.1 christos dns_name_t *name,
538 1.1 christos dns_rdataset_t *neg,
539 1.1 christos dns_rdataset_t *negsig);
540 1.1 christos static isc_result_t rdataset_getclosest(dns_rdataset_t *rdataset,
541 1.1 christos dns_name_t *name,
542 1.1 christos dns_rdataset_t *neg,
543 1.1 christos dns_rdataset_t *negsig);
544 1.3 christos static inline bool need_headerupdate(rdatasetheader_t *header,
545 1.1 christos isc_stdtime_t now);
546 1.1 christos static void update_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
547 1.1 christos isc_stdtime_t now);
548 1.1 christos static void expire_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
549 1.3 christos bool tree_locked, expire_t reason);
550 1.1 christos static void overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start,
551 1.3 christos isc_stdtime_t now, bool tree_locked);
552 1.1 christos static isc_result_t resign_insert(dns_rbtdb_t *rbtdb, int idx,
553 1.1 christos rdatasetheader_t *newheader);
554 1.1 christos static void resign_delete(dns_rbtdb_t *rbtdb, rbtdb_version_t *version,
555 1.1 christos rdatasetheader_t *header);
556 1.1 christos static void prune_tree(isc_task_t *task, isc_event_t *event);
557 1.1 christos static void rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust);
558 1.1 christos static void rdataset_expire(dns_rdataset_t *rdataset);
559 1.1 christos static void rdataset_clearprefetch(dns_rdataset_t *rdataset);
560 1.1 christos static void rdataset_setownercase(dns_rdataset_t *rdataset,
561 1.1 christos const dns_name_t *name);
562 1.1 christos static void rdataset_getownercase(const dns_rdataset_t *rdataset,
563 1.1 christos dns_name_t *name);
564 1.1 christos static isc_result_t rdataset_addglue(dns_rdataset_t *rdataset,
565 1.1 christos dns_dbversion_t *version,
566 1.1 christos dns_message_t *msg);
567 1.1 christos static void free_gluetable(rbtdb_version_t *version);
568 1.1 christos
569 1.1 christos static dns_rdatasetmethods_t rdataset_methods = {
570 1.1 christos rdataset_disassociate,
571 1.1 christos rdataset_first,
572 1.1 christos rdataset_next,
573 1.1 christos rdataset_current,
574 1.1 christos rdataset_clone,
575 1.1 christos rdataset_count,
576 1.1 christos NULL, /* addnoqname */
577 1.1 christos rdataset_getnoqname,
578 1.1 christos NULL, /* addclosest */
579 1.1 christos rdataset_getclosest,
580 1.1 christos rdataset_settrust,
581 1.1 christos rdataset_expire,
582 1.1 christos rdataset_clearprefetch,
583 1.1 christos rdataset_setownercase,
584 1.1 christos rdataset_getownercase,
585 1.1 christos rdataset_addglue
586 1.1 christos };
587 1.1 christos
588 1.1 christos static dns_rdatasetmethods_t slab_methods = {
589 1.1 christos rdataset_disassociate,
590 1.1 christos rdataset_first,
591 1.1 christos rdataset_next,
592 1.1 christos rdataset_current,
593 1.1 christos rdataset_clone,
594 1.1 christos rdataset_count,
595 1.1 christos NULL, /* addnoqname */
596 1.1 christos NULL, /* getnoqname */
597 1.1 christos NULL, /* addclosest */
598 1.1 christos NULL, /* getclosest */
599 1.1 christos NULL, /* settrust */
600 1.1 christos NULL, /* expire */
601 1.1 christos NULL, /* clearprefetch */
602 1.1 christos NULL, /* setownercase */
603 1.1 christos NULL, /* getownercase */
604 1.1 christos NULL /* addglue */
605 1.1 christos };
606 1.1 christos
607 1.1 christos static void rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp);
608 1.1 christos static isc_result_t rdatasetiter_first(dns_rdatasetiter_t *iterator);
609 1.1 christos static isc_result_t rdatasetiter_next(dns_rdatasetiter_t *iterator);
610 1.1 christos static void rdatasetiter_current(dns_rdatasetiter_t *iterator,
611 1.1 christos dns_rdataset_t *rdataset);
612 1.1 christos
613 1.1 christos static dns_rdatasetitermethods_t rdatasetiter_methods = {
614 1.1 christos rdatasetiter_destroy,
615 1.1 christos rdatasetiter_first,
616 1.1 christos rdatasetiter_next,
617 1.1 christos rdatasetiter_current
618 1.1 christos };
619 1.1 christos
620 1.1 christos typedef struct rbtdb_rdatasetiter {
621 1.1 christos dns_rdatasetiter_t common;
622 1.1 christos rdatasetheader_t * current;
623 1.1 christos } rbtdb_rdatasetiter_t;
624 1.1 christos
625 1.1 christos /*
626 1.1 christos * Note that these iterators, unless created with either DNS_DB_NSEC3ONLY or
627 1.1 christos * DNS_DB_NONSEC3, will transparently move between the last node of the
628 1.1 christos * "regular" RBT ("chain" field) and the root node of the NSEC3 RBT
629 1.1 christos * ("nsec3chain" field) of the database in question, as if the latter was a
630 1.1 christos * successor to the former in lexical order. The "current" field always holds
631 1.1 christos * the address of either "chain" or "nsec3chain", depending on which RBT is
632 1.1 christos * being traversed at given time.
633 1.1 christos */
634 1.1 christos static void dbiterator_destroy(dns_dbiterator_t **iteratorp);
635 1.1 christos static isc_result_t dbiterator_first(dns_dbiterator_t *iterator);
636 1.1 christos static isc_result_t dbiterator_last(dns_dbiterator_t *iterator);
637 1.1 christos static isc_result_t dbiterator_seek(dns_dbiterator_t *iterator,
638 1.1 christos const dns_name_t *name);
639 1.1 christos static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator);
640 1.1 christos static isc_result_t dbiterator_next(dns_dbiterator_t *iterator);
641 1.1 christos static isc_result_t dbiterator_current(dns_dbiterator_t *iterator,
642 1.1 christos dns_dbnode_t **nodep,
643 1.1 christos dns_name_t *name);
644 1.1 christos static isc_result_t dbiterator_pause(dns_dbiterator_t *iterator);
645 1.1 christos static isc_result_t dbiterator_origin(dns_dbiterator_t *iterator,
646 1.1 christos dns_name_t *name);
647 1.1 christos
648 1.1 christos static dns_dbiteratormethods_t dbiterator_methods = {
649 1.1 christos dbiterator_destroy,
650 1.1 christos dbiterator_first,
651 1.1 christos dbiterator_last,
652 1.1 christos dbiterator_seek,
653 1.1 christos dbiterator_prev,
654 1.1 christos dbiterator_next,
655 1.1 christos dbiterator_current,
656 1.1 christos dbiterator_pause,
657 1.1 christos dbiterator_origin
658 1.1 christos };
659 1.1 christos
660 1.1 christos #define DELETION_BATCH_MAX 64
661 1.1 christos
662 1.1 christos /*
663 1.3 christos * If 'paused' is true, then the tree lock is not being held.
664 1.1 christos */
665 1.1 christos typedef struct rbtdb_dbiterator {
666 1.1 christos dns_dbiterator_t common;
667 1.3 christos bool paused;
668 1.3 christos bool new_origin;
669 1.1 christos isc_rwlocktype_t tree_locked;
670 1.1 christos isc_result_t result;
671 1.1 christos dns_fixedname_t name;
672 1.1 christos dns_fixedname_t origin;
673 1.1 christos dns_rbtnodechain_t chain;
674 1.1 christos dns_rbtnodechain_t nsec3chain;
675 1.1 christos dns_rbtnodechain_t *current;
676 1.1 christos dns_rbtnode_t *node;
677 1.1 christos dns_rbtnode_t *deletions[DELETION_BATCH_MAX];
678 1.1 christos int delcnt;
679 1.3 christos bool nsec3only;
680 1.3 christos bool nonsec3;
681 1.1 christos } rbtdb_dbiterator_t;
682 1.1 christos
683 1.1 christos
684 1.1 christos #define IS_STUB(rbtdb) (((rbtdb)->common.attributes & DNS_DBATTR_STUB) != 0)
685 1.1 christos #define IS_CACHE(rbtdb) (((rbtdb)->common.attributes & DNS_DBATTR_CACHE) != 0)
686 1.1 christos
687 1.3 christos static void free_rbtdb(dns_rbtdb_t *rbtdb, bool log,
688 1.1 christos isc_event_t *event);
689 1.3 christos static void overmem(dns_db_t *db, bool over);
690 1.1 christos static void setnsec3parameters(dns_db_t *db, rbtdb_version_t *version);
691 1.1 christos static void setownercase(rdatasetheader_t *header, const dns_name_t *name);
692 1.1 christos
693 1.3 christos static bool match_header_version(rbtdb_file_header_t *header);
694 1.1 christos
695 1.1 christos /* Pad to 32 bytes */
696 1.1 christos static char FILE_VERSION[32] = "\0";
697 1.1 christos
698 1.1 christos /*%
699 1.1 christos * 'init_count' is used to initialize 'newheader->count' which inturn
700 1.1 christos * is used to determine where in the cycle rrset-order cyclic starts.
701 1.1 christos * We don't lock this as we don't care about simultaneous updates.
702 1.1 christos *
703 1.1 christos * Note:
704 1.3 christos * Both init_count and header->count can be UINT32_MAX.
705 1.1 christos * The count on the returned rdataset however can't be as
706 1.1 christos * that indicates that the database does not implement cyclic
707 1.1 christos * processing.
708 1.1 christos */
709 1.1 christos static unsigned int init_count;
710 1.1 christos
711 1.1 christos /*
712 1.1 christos * Locking
713 1.1 christos *
714 1.1 christos * If a routine is going to lock more than one lock in this module, then
715 1.1 christos * the locking must be done in the following order:
716 1.1 christos *
717 1.1 christos * Tree Lock
718 1.1 christos *
719 1.1 christos * Node Lock (Only one from the set may be locked at one time by
720 1.1 christos * any caller)
721 1.1 christos *
722 1.1 christos * Database Lock
723 1.1 christos *
724 1.1 christos * Failure to follow this hierarchy can result in deadlock.
725 1.1 christos */
726 1.1 christos
727 1.1 christos /*
728 1.1 christos * Deleting Nodes
729 1.1 christos *
730 1.1 christos * For zone databases the node for the origin of the zone MUST NOT be deleted.
731 1.1 christos */
732 1.1 christos
733 1.1 christos /*
734 1.1 christos * Debugging routines
735 1.1 christos */
736 1.1 christos #ifdef DEBUG
737 1.1 christos static void
738 1.1 christos hexdump(const char *desc, unsigned char *data, size_t size) {
739 1.1 christos char hexdump[BUFSIZ * 2 + 1];
740 1.1 christos isc_buffer_t b;
741 1.1 christos isc_region_t r;
742 1.1 christos isc_result_t result;
743 1.1 christos size_t bytes;
744 1.1 christos
745 1.1 christos fprintf(stderr, "%s: ", desc);
746 1.1 christos do {
747 1.1 christos isc_buffer_init(&b, hexdump, sizeof(hexdump));
748 1.1 christos r.base = data;
749 1.1 christos r.length = bytes = (size > BUFSIZ) ? BUFSIZ : size;
750 1.1 christos result = isc_hex_totext(&r, 0, "", &b);
751 1.1 christos RUNTIME_CHECK(result == ISC_R_SUCCESS);
752 1.1 christos isc_buffer_putuint8(&b, 0);
753 1.1 christos fprintf(stderr, "%s", hexdump);
754 1.1 christos data += bytes;
755 1.1 christos size -= bytes;
756 1.1 christos } while (size > 0);
757 1.1 christos fprintf(stderr, "\n");
758 1.1 christos }
759 1.1 christos #endif
760 1.1 christos
761 1.1 christos
762 1.1 christos /*
763 1.1 christos * DB Routines
764 1.1 christos */
765 1.1 christos
766 1.1 christos static void
767 1.1 christos attach(dns_db_t *source, dns_db_t **targetp) {
768 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)source;
769 1.1 christos
770 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
771 1.1 christos
772 1.3 christos isc_refcount_increment(&rbtdb->references);
773 1.1 christos
774 1.1 christos *targetp = source;
775 1.1 christos }
776 1.1 christos
777 1.1 christos static void
778 1.1 christos free_rbtdb_callback(isc_task_t *task, isc_event_t *event) {
779 1.1 christos dns_rbtdb_t *rbtdb = event->ev_arg;
780 1.1 christos
781 1.1 christos UNUSED(task);
782 1.1 christos
783 1.3 christos free_rbtdb(rbtdb, true, event);
784 1.1 christos }
785 1.1 christos
786 1.1 christos static void
787 1.1 christos update_cachestats(dns_rbtdb_t *rbtdb, isc_result_t result) {
788 1.1 christos INSIST(IS_CACHE(rbtdb));
789 1.1 christos
790 1.1 christos if (rbtdb->cachestats == NULL)
791 1.1 christos return;
792 1.1 christos
793 1.1 christos switch (result) {
794 1.1 christos case ISC_R_SUCCESS:
795 1.1 christos case DNS_R_CNAME:
796 1.1 christos case DNS_R_DNAME:
797 1.1 christos case DNS_R_DELEGATION:
798 1.1 christos case DNS_R_NCACHENXDOMAIN:
799 1.1 christos case DNS_R_NCACHENXRRSET:
800 1.1 christos isc_stats_increment(rbtdb->cachestats,
801 1.1 christos dns_cachestatscounter_hits);
802 1.1 christos break;
803 1.1 christos default:
804 1.1 christos isc_stats_increment(rbtdb->cachestats,
805 1.1 christos dns_cachestatscounter_misses);
806 1.1 christos }
807 1.1 christos }
808 1.1 christos
809 1.1 christos static void
810 1.1 christos update_rrsetstats(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
811 1.3 christos bool increment)
812 1.1 christos {
813 1.1 christos dns_rdatastatstype_t statattributes = 0;
814 1.1 christos dns_rdatastatstype_t base = 0;
815 1.1 christos dns_rdatastatstype_t type;
816 1.1 christos
817 1.1 christos /* At the moment we count statistics only for cache DB */
818 1.1 christos INSIST(IS_CACHE(rbtdb));
819 1.1 christos
820 1.1 christos if (NEGATIVE(header)) {
821 1.1 christos if (NXDOMAIN(header))
822 1.1 christos statattributes = DNS_RDATASTATSTYPE_ATTR_NXDOMAIN;
823 1.1 christos else {
824 1.1 christos statattributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET;
825 1.1 christos base = RBTDB_RDATATYPE_EXT(header->type);
826 1.1 christos }
827 1.1 christos } else
828 1.1 christos base = RBTDB_RDATATYPE_BASE(header->type);
829 1.1 christos
830 1.1 christos if (STALE(header))
831 1.1 christos statattributes |= DNS_RDATASTATSTYPE_ATTR_STALE;
832 1.1 christos
833 1.1 christos type = DNS_RDATASTATSTYPE_VALUE(base, statattributes);
834 1.1 christos if (increment)
835 1.1 christos dns_rdatasetstats_increment(rbtdb->rrsetstats, type);
836 1.1 christos else
837 1.1 christos dns_rdatasetstats_decrement(rbtdb->rrsetstats, type);
838 1.1 christos }
839 1.1 christos
840 1.1 christos static void
841 1.1 christos set_ttl(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, dns_ttl_t newttl) {
842 1.1 christos int idx;
843 1.1 christos isc_heap_t *heap;
844 1.1 christos dns_ttl_t oldttl;
845 1.1 christos
846 1.1 christos
847 1.1 christos if (!IS_CACHE(rbtdb)) {
848 1.1 christos header->rdh_ttl = newttl;
849 1.1 christos return;
850 1.1 christos }
851 1.1 christos
852 1.1 christos oldttl = header->rdh_ttl;
853 1.1 christos header->rdh_ttl = newttl;
854 1.1 christos
855 1.1 christos /*
856 1.1 christos * It's possible the rbtdb is not a cache. If this is the case,
857 1.1 christos * we will not have a heap, and we move on. If we do, though,
858 1.1 christos * we might need to adjust things.
859 1.1 christos */
860 1.1 christos if (header->heap_index == 0 || newttl == oldttl)
861 1.1 christos return;
862 1.1 christos idx = header->node->locknum;
863 1.1 christos if (rbtdb->heaps == NULL || rbtdb->heaps[idx] == NULL)
864 1.1 christos return;
865 1.1 christos heap = rbtdb->heaps[idx];
866 1.1 christos
867 1.1 christos if (newttl < oldttl)
868 1.1 christos isc_heap_increased(heap, header->heap_index);
869 1.1 christos else
870 1.1 christos isc_heap_decreased(heap, header->heap_index);
871 1.1 christos }
872 1.1 christos
873 1.1 christos /*%
874 1.1 christos * These functions allow the heap code to rank the priority of each
875 1.3 christos * element. It returns true if v1 happens "sooner" than v2.
876 1.1 christos */
877 1.3 christos static bool
878 1.1 christos ttl_sooner(void *v1, void *v2) {
879 1.1 christos rdatasetheader_t *h1 = v1;
880 1.1 christos rdatasetheader_t *h2 = v2;
881 1.1 christos
882 1.3 christos return (h1->rdh_ttl < h2->rdh_ttl);
883 1.1 christos }
884 1.1 christos
885 1.3 christos static bool
886 1.1 christos resign_sooner(void *v1, void *v2) {
887 1.1 christos rdatasetheader_t *h1 = v1;
888 1.1 christos rdatasetheader_t *h2 = v2;
889 1.1 christos
890 1.3 christos return (h1->resign < h2->resign ||
891 1.3 christos (h1->resign == h2->resign &&
892 1.3 christos h1->resign_lsb < h2->resign_lsb));
893 1.1 christos }
894 1.1 christos
895 1.1 christos /*%
896 1.1 christos * This function sets the heap index into the header.
897 1.1 christos */
898 1.1 christos static void
899 1.1 christos set_index(void *what, unsigned int idx) {
900 1.1 christos rdatasetheader_t *h = what;
901 1.1 christos
902 1.1 christos h->heap_index = idx;
903 1.1 christos }
904 1.1 christos
905 1.1 christos /*%
906 1.1 christos * Work out how many nodes can be deleted in the time between two
907 1.1 christos * requests to the nameserver. Smooth the resulting number and use it
908 1.1 christos * as a estimate for the number of nodes to be deleted in the next
909 1.1 christos * iteration.
910 1.1 christos */
911 1.1 christos static unsigned int
912 1.1 christos adjust_quantum(unsigned int old, isc_time_t *start) {
913 1.1 christos unsigned int pps = dns_pps; /* packets per second */
914 1.1 christos unsigned int interval;
915 1.3 christos uint64_t usecs;
916 1.1 christos isc_time_t end;
917 1.1 christos unsigned int nodes;
918 1.1 christos
919 1.1 christos if (pps < 100)
920 1.1 christos pps = 100;
921 1.1 christos isc_time_now(&end);
922 1.1 christos
923 1.1 christos interval = 1000000 / pps; /* interval in usec */
924 1.1 christos if (interval == 0)
925 1.1 christos interval = 1;
926 1.1 christos usecs = isc_time_microdiff(&end, start);
927 1.1 christos if (usecs == 0) {
928 1.1 christos /*
929 1.1 christos * We were unable to measure the amount of time taken.
930 1.1 christos * Double the nodes deleted next time.
931 1.1 christos */
932 1.1 christos old *= 2;
933 1.1 christos if (old > 1000)
934 1.1 christos old = 1000;
935 1.1 christos return (old);
936 1.1 christos }
937 1.1 christos nodes = old * interval;
938 1.1 christos nodes /= (unsigned int)usecs;
939 1.1 christos if (nodes == 0)
940 1.1 christos nodes = 1;
941 1.1 christos else if (nodes > 1000)
942 1.1 christos nodes = 1000;
943 1.1 christos
944 1.1 christos /* Smooth */
945 1.1 christos nodes = (nodes + old * 3) / 4;
946 1.1 christos
947 1.1 christos if (nodes != old)
948 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
949 1.1 christos DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
950 1.1 christos "adjust_quantum: old=%d, new=%d", old, nodes);
951 1.1 christos
952 1.1 christos return (nodes);
953 1.1 christos }
954 1.1 christos
955 1.1 christos static void
956 1.3 christos free_rbtdb(dns_rbtdb_t *rbtdb, bool log, isc_event_t *event) {
957 1.1 christos unsigned int i;
958 1.1 christos isc_result_t result;
959 1.1 christos char buf[DNS_NAME_FORMATSIZE];
960 1.1 christos dns_rbt_t **treep;
961 1.1 christos isc_time_t start;
962 1.1 christos dns_dbonupdatelistener_t *listener, *listener_next;
963 1.1 christos
964 1.1 christos if (IS_CACHE(rbtdb) && rbtdb->common.rdclass == dns_rdataclass_in)
965 1.3 christos overmem((dns_db_t *)rbtdb, (bool)-1);
966 1.1 christos
967 1.1 christos REQUIRE(rbtdb->current_version != NULL || EMPTY(rbtdb->open_versions));
968 1.1 christos REQUIRE(rbtdb->future_version == NULL);
969 1.1 christos
970 1.1 christos if (rbtdb->current_version != NULL) {
971 1.1 christos
972 1.3 christos INSIST(isc_refcount_decrement(
973 1.3 christos &rbtdb->current_version->references)
974 1.3 christos == 1);
975 1.3 christos
976 1.1 christos UNLINK(rbtdb->open_versions, rbtdb->current_version, link);
977 1.1 christos isc_rwlock_destroy(&rbtdb->current_version->glue_rwlock);
978 1.1 christos isc_refcount_destroy(&rbtdb->current_version->references);
979 1.1 christos isc_rwlock_destroy(&rbtdb->current_version->rwlock);
980 1.1 christos isc_mem_put(rbtdb->common.mctx, rbtdb->current_version,
981 1.1 christos sizeof(rbtdb_version_t));
982 1.1 christos }
983 1.1 christos
984 1.1 christos /*
985 1.1 christos * We assume the number of remaining dead nodes is reasonably small;
986 1.1 christos * the overhead of unlinking all nodes here should be negligible.
987 1.1 christos */
988 1.1 christos for (i = 0; i < rbtdb->node_lock_count; i++) {
989 1.1 christos dns_rbtnode_t *node;
990 1.1 christos
991 1.1 christos node = ISC_LIST_HEAD(rbtdb->deadnodes[i]);
992 1.1 christos while (node != NULL) {
993 1.1 christos ISC_LIST_UNLINK(rbtdb->deadnodes[i], node, deadlink);
994 1.1 christos node = ISC_LIST_HEAD(rbtdb->deadnodes[i]);
995 1.1 christos }
996 1.1 christos }
997 1.1 christos
998 1.1 christos if (event == NULL)
999 1.1 christos rbtdb->quantum = (rbtdb->task != NULL) ? 100 : 0;
1000 1.1 christos
1001 1.1 christos for (;;) {
1002 1.1 christos /*
1003 1.1 christos * pick the next tree to (start to) destroy
1004 1.1 christos */
1005 1.1 christos treep = &rbtdb->tree;
1006 1.1 christos if (*treep == NULL) {
1007 1.1 christos treep = &rbtdb->nsec;
1008 1.1 christos if (*treep == NULL) {
1009 1.1 christos treep = &rbtdb->nsec3;
1010 1.1 christos /*
1011 1.1 christos * we're finished after clear cutting
1012 1.1 christos */
1013 1.1 christos if (*treep == NULL)
1014 1.1 christos break;
1015 1.1 christos }
1016 1.1 christos }
1017 1.1 christos
1018 1.1 christos isc_time_now(&start);
1019 1.1 christos result = dns_rbt_destroy2(treep, rbtdb->quantum);
1020 1.1 christos if (result == ISC_R_QUOTA) {
1021 1.1 christos INSIST(rbtdb->task != NULL);
1022 1.1 christos if (rbtdb->quantum != 0)
1023 1.1 christos rbtdb->quantum = adjust_quantum(rbtdb->quantum,
1024 1.1 christos &start);
1025 1.1 christos if (event == NULL)
1026 1.1 christos event = isc_event_allocate(rbtdb->common.mctx,
1027 1.1 christos NULL,
1028 1.1 christos DNS_EVENT_FREESTORAGE,
1029 1.1 christos free_rbtdb_callback,
1030 1.1 christos rbtdb,
1031 1.1 christos sizeof(isc_event_t));
1032 1.1 christos if (event == NULL)
1033 1.1 christos continue;
1034 1.1 christos isc_task_send(rbtdb->task, &event);
1035 1.1 christos return;
1036 1.1 christos }
1037 1.1 christos INSIST(result == ISC_R_SUCCESS && *treep == NULL);
1038 1.1 christos }
1039 1.1 christos
1040 1.1 christos if (event != NULL)
1041 1.1 christos isc_event_free(&event);
1042 1.1 christos if (log) {
1043 1.1 christos if (dns_name_dynamic(&rbtdb->common.origin))
1044 1.1 christos dns_name_format(&rbtdb->common.origin, buf,
1045 1.1 christos sizeof(buf));
1046 1.1 christos else
1047 1.1 christos strlcpy(buf, "<UNKNOWN>", sizeof(buf));
1048 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1049 1.1 christos DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
1050 1.1 christos "done free_rbtdb(%s)", buf);
1051 1.1 christos }
1052 1.1 christos if (dns_name_dynamic(&rbtdb->common.origin))
1053 1.1 christos dns_name_free(&rbtdb->common.origin, rbtdb->common.mctx);
1054 1.1 christos for (i = 0; i < rbtdb->node_lock_count; i++) {
1055 1.1 christos isc_refcount_destroy(&rbtdb->node_locks[i].references);
1056 1.1 christos NODE_DESTROYLOCK(&rbtdb->node_locks[i].lock);
1057 1.1 christos }
1058 1.1 christos
1059 1.1 christos /*
1060 1.1 christos * Clean up LRU / re-signing order lists.
1061 1.1 christos */
1062 1.1 christos if (rbtdb->rdatasets != NULL) {
1063 1.1 christos for (i = 0; i < rbtdb->node_lock_count; i++)
1064 1.1 christos INSIST(ISC_LIST_EMPTY(rbtdb->rdatasets[i]));
1065 1.1 christos isc_mem_put(rbtdb->common.mctx, rbtdb->rdatasets,
1066 1.1 christos rbtdb->node_lock_count *
1067 1.1 christos sizeof(rdatasetheaderlist_t));
1068 1.1 christos }
1069 1.1 christos /*
1070 1.1 christos * Clean up dead node buckets.
1071 1.1 christos */
1072 1.1 christos if (rbtdb->deadnodes != NULL) {
1073 1.1 christos for (i = 0; i < rbtdb->node_lock_count; i++)
1074 1.1 christos INSIST(ISC_LIST_EMPTY(rbtdb->deadnodes[i]));
1075 1.1 christos isc_mem_put(rbtdb->common.mctx, rbtdb->deadnodes,
1076 1.1 christos rbtdb->node_lock_count * sizeof(rbtnodelist_t));
1077 1.1 christos }
1078 1.1 christos /*
1079 1.1 christos * Clean up heap objects.
1080 1.1 christos */
1081 1.1 christos if (rbtdb->heaps != NULL) {
1082 1.1 christos for (i = 0; i < rbtdb->node_lock_count; i++)
1083 1.1 christos isc_heap_destroy(&rbtdb->heaps[i]);
1084 1.1 christos isc_mem_put(rbtdb->hmctx, rbtdb->heaps,
1085 1.1 christos rbtdb->node_lock_count * sizeof(isc_heap_t *));
1086 1.1 christos }
1087 1.1 christos
1088 1.1 christos if (rbtdb->rrsetstats != NULL)
1089 1.1 christos dns_stats_detach(&rbtdb->rrsetstats);
1090 1.1 christos if (rbtdb->cachestats != NULL)
1091 1.1 christos isc_stats_detach(&rbtdb->cachestats);
1092 1.1 christos if (rbtdb->gluecachestats != NULL)
1093 1.1 christos isc_stats_detach(&rbtdb->gluecachestats);
1094 1.1 christos
1095 1.1 christos isc_mem_put(rbtdb->common.mctx, rbtdb->node_locks,
1096 1.1 christos rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t));
1097 1.1 christos isc_rwlock_destroy(&rbtdb->tree_lock);
1098 1.1 christos isc_refcount_destroy(&rbtdb->references);
1099 1.1 christos if (rbtdb->task != NULL)
1100 1.1 christos isc_task_detach(&rbtdb->task);
1101 1.1 christos
1102 1.1 christos RBTDB_DESTROYLOCK(&rbtdb->lock);
1103 1.1 christos rbtdb->common.magic = 0;
1104 1.1 christos rbtdb->common.impmagic = 0;
1105 1.1 christos isc_mem_detach(&rbtdb->hmctx);
1106 1.1 christos
1107 1.1 christos if (rbtdb->mmap_location != NULL)
1108 1.1 christos isc_file_munmap(rbtdb->mmap_location,
1109 1.1 christos (size_t) rbtdb->mmap_size);
1110 1.1 christos
1111 1.1 christos for (listener = ISC_LIST_HEAD(rbtdb->common.update_listeners);
1112 1.1 christos listener != NULL;
1113 1.1 christos listener = listener_next)
1114 1.1 christos {
1115 1.1 christos listener_next = ISC_LIST_NEXT(listener, link);
1116 1.1 christos ISC_LIST_UNLINK(rbtdb->common.update_listeners, listener, link);
1117 1.1 christos isc_mem_put(rbtdb->common.mctx, listener,
1118 1.1 christos sizeof(dns_dbonupdatelistener_t));
1119 1.1 christos }
1120 1.1 christos
1121 1.1 christos isc_mem_putanddetach(&rbtdb->common.mctx, rbtdb, sizeof(*rbtdb));
1122 1.1 christos }
1123 1.1 christos
1124 1.1 christos static inline void
1125 1.1 christos maybe_free_rbtdb(dns_rbtdb_t *rbtdb) {
1126 1.3 christos bool want_free = false;
1127 1.1 christos unsigned int i;
1128 1.1 christos unsigned int inactive = 0;
1129 1.1 christos
1130 1.1 christos /* XXX check for open versions here */
1131 1.1 christos
1132 1.1 christos if (rbtdb->soanode != NULL)
1133 1.1 christos dns_db_detachnode((dns_db_t *)rbtdb, &rbtdb->soanode);
1134 1.1 christos if (rbtdb->nsnode != NULL)
1135 1.1 christos dns_db_detachnode((dns_db_t *)rbtdb, &rbtdb->nsnode);
1136 1.1 christos
1137 1.1 christos /*
1138 1.1 christos * The current version's glue table needs to be freed early
1139 1.1 christos * so the nodes are dereferenced before we check the active
1140 1.1 christos * node count below.
1141 1.1 christos */
1142 1.1 christos if (rbtdb->current_version != NULL) {
1143 1.1 christos free_gluetable(rbtdb->current_version);
1144 1.1 christos }
1145 1.1 christos
1146 1.1 christos /*
1147 1.1 christos * Even though there are no external direct references, there still
1148 1.1 christos * may be nodes in use.
1149 1.1 christos */
1150 1.1 christos for (i = 0; i < rbtdb->node_lock_count; i++) {
1151 1.1 christos NODE_LOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_write);
1152 1.3 christos rbtdb->node_locks[i].exiting = true;
1153 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_write);
1154 1.1 christos if (isc_refcount_current(&rbtdb->node_locks[i].references) == 0)
1155 1.1 christos {
1156 1.1 christos inactive++;
1157 1.1 christos }
1158 1.1 christos }
1159 1.1 christos
1160 1.1 christos if (inactive != 0) {
1161 1.1 christos RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
1162 1.1 christos rbtdb->active -= inactive;
1163 1.1 christos if (rbtdb->active == 0) {
1164 1.3 christos want_free = true;
1165 1.1 christos }
1166 1.1 christos RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
1167 1.1 christos if (want_free) {
1168 1.1 christos char buf[DNS_NAME_FORMATSIZE];
1169 1.1 christos if (dns_name_dynamic(&rbtdb->common.origin)) {
1170 1.1 christos dns_name_format(&rbtdb->common.origin, buf,
1171 1.1 christos sizeof(buf));
1172 1.1 christos } else {
1173 1.1 christos strlcpy(buf, "<UNKNOWN>", sizeof(buf));
1174 1.1 christos }
1175 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1176 1.1 christos DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
1177 1.1 christos "calling free_rbtdb(%s)", buf);
1178 1.3 christos free_rbtdb(rbtdb, true, NULL);
1179 1.1 christos }
1180 1.1 christos }
1181 1.1 christos }
1182 1.1 christos
1183 1.1 christos static void
1184 1.1 christos detach(dns_db_t **dbp) {
1185 1.3 christos REQUIRE(dbp != NULL && VALID_RBTDB((dns_rbtdb_t *)(*dbp)));
1186 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(*dbp);
1187 1.3 christos *dbp = NULL;
1188 1.1 christos
1189 1.3 christos if (isc_refcount_decrement(&rbtdb->references) == 1) {
1190 1.3 christos (void)isc_refcount_current(&rbtdb->references);
1191 1.1 christos maybe_free_rbtdb(rbtdb);
1192 1.3 christos }
1193 1.1 christos }
1194 1.1 christos
1195 1.1 christos static void
1196 1.1 christos currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
1197 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
1198 1.1 christos rbtdb_version_t *version;
1199 1.1 christos
1200 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
1201 1.1 christos
1202 1.3 christos /* XXXOND: Is the lock needed here? */
1203 1.1 christos RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read);
1204 1.1 christos version = rbtdb->current_version;
1205 1.3 christos isc_refcount_increment(&version->references);
1206 1.1 christos RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read);
1207 1.1 christos
1208 1.1 christos *versionp = (dns_dbversion_t *)version;
1209 1.1 christos }
1210 1.1 christos
1211 1.1 christos static inline rbtdb_version_t *
1212 1.1 christos allocate_version(isc_mem_t *mctx, rbtdb_serial_t serial,
1213 1.3 christos unsigned int references, bool writer)
1214 1.1 christos {
1215 1.1 christos isc_result_t result;
1216 1.1 christos rbtdb_version_t *version;
1217 1.1 christos size_t i;
1218 1.1 christos
1219 1.1 christos version = isc_mem_get(mctx, sizeof(*version));
1220 1.1 christos if (version == NULL)
1221 1.1 christos return (NULL);
1222 1.1 christos version->serial = serial;
1223 1.3 christos
1224 1.3 christos isc_refcount_init(&version->references, references);
1225 1.3 christos
1226 1.1 christos result = isc_rwlock_init(&version->glue_rwlock, 0, 0);
1227 1.1 christos if (result != ISC_R_SUCCESS) {
1228 1.1 christos isc_refcount_destroy(&version->references);
1229 1.1 christos isc_mem_put(mctx, version, sizeof(*version));
1230 1.1 christos return (NULL);
1231 1.1 christos }
1232 1.1 christos
1233 1.1 christos version->glue_table_size = RBTDB_GLUE_TABLE_INIT_SIZE;
1234 1.1 christos version->glue_table_nodecount = 0U;
1235 1.1 christos version->glue_table = (rbtdb_glue_table_node_t **)
1236 1.1 christos isc_mem_get(mctx, (version->glue_table_size *
1237 1.1 christos sizeof(*version->glue_table)));
1238 1.1 christos if (version->glue_table == NULL) {
1239 1.1 christos isc_rwlock_destroy(&version->glue_rwlock);
1240 1.1 christos isc_refcount_destroy(&version->references);
1241 1.1 christos isc_mem_put(mctx, version, sizeof(*version));
1242 1.1 christos return (NULL);
1243 1.1 christos }
1244 1.1 christos
1245 1.1 christos version->writer = writer;
1246 1.3 christos version->commit_ok = false;
1247 1.1 christos ISC_LIST_INIT(version->changed_list);
1248 1.1 christos ISC_LIST_INIT(version->resigned_list);
1249 1.1 christos ISC_LINK_INIT(version, link);
1250 1.1 christos
1251 1.1 christos for (i = 0; i < version->glue_table_size; i++)
1252 1.1 christos version->glue_table[i] = NULL;
1253 1.1 christos
1254 1.1 christos return (version);
1255 1.1 christos }
1256 1.1 christos
1257 1.1 christos static isc_result_t
1258 1.1 christos newversion(dns_db_t *db, dns_dbversion_t **versionp) {
1259 1.1 christos isc_result_t result;
1260 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
1261 1.1 christos rbtdb_version_t *version;
1262 1.1 christos
1263 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
1264 1.1 christos REQUIRE(versionp != NULL && *versionp == NULL);
1265 1.1 christos REQUIRE(rbtdb->future_version == NULL);
1266 1.1 christos
1267 1.1 christos RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
1268 1.1 christos RUNTIME_CHECK(rbtdb->next_serial != 0); /* XXX Error? */
1269 1.1 christos version = allocate_version(rbtdb->common.mctx, rbtdb->next_serial, 1,
1270 1.3 christos true);
1271 1.1 christos if (version != NULL) {
1272 1.1 christos version->rbtdb = rbtdb;
1273 1.3 christos version->commit_ok = true;
1274 1.1 christos version->secure = rbtdb->current_version->secure;
1275 1.1 christos version->havensec3 = rbtdb->current_version->havensec3;
1276 1.1 christos if (version->havensec3) {
1277 1.1 christos version->flags = rbtdb->current_version->flags;
1278 1.1 christos version->iterations =
1279 1.1 christos rbtdb->current_version->iterations;
1280 1.1 christos version->hash = rbtdb->current_version->hash;
1281 1.1 christos version->salt_length =
1282 1.1 christos rbtdb->current_version->salt_length;
1283 1.1 christos memmove(version->salt, rbtdb->current_version->salt,
1284 1.1 christos version->salt_length);
1285 1.1 christos } else {
1286 1.1 christos version->flags = 0;
1287 1.1 christos version->iterations = 0;
1288 1.1 christos version->hash = 0;
1289 1.1 christos version->salt_length = 0;
1290 1.1 christos memset(version->salt, 0, sizeof(version->salt));
1291 1.1 christos }
1292 1.1 christos result = isc_rwlock_init(&version->rwlock, 0, 0);
1293 1.1 christos if (result != ISC_R_SUCCESS) {
1294 1.1 christos free_gluetable(version);
1295 1.1 christos isc_rwlock_destroy(&version->glue_rwlock);
1296 1.1 christos isc_refcount_destroy(&version->references);
1297 1.1 christos isc_mem_put(rbtdb->common.mctx, version,
1298 1.1 christos sizeof(*version));
1299 1.1 christos version = NULL;
1300 1.1 christos } else {
1301 1.1 christos RWLOCK(&rbtdb->current_version->rwlock,
1302 1.1 christos isc_rwlocktype_read);
1303 1.1 christos version->records = rbtdb->current_version->records;
1304 1.1 christos version->bytes = rbtdb->current_version->bytes;
1305 1.1 christos RWUNLOCK(&rbtdb->current_version->rwlock,
1306 1.1 christos isc_rwlocktype_read);
1307 1.1 christos rbtdb->next_serial++;
1308 1.1 christos rbtdb->future_version = version;
1309 1.1 christos }
1310 1.1 christos } else
1311 1.1 christos result = ISC_R_NOMEMORY;
1312 1.1 christos RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
1313 1.1 christos
1314 1.1 christos if (version == NULL)
1315 1.1 christos return (result);
1316 1.1 christos
1317 1.1 christos *versionp = version;
1318 1.1 christos
1319 1.1 christos return (ISC_R_SUCCESS);
1320 1.1 christos }
1321 1.1 christos
1322 1.1 christos static void
1323 1.1 christos attachversion(dns_db_t *db, dns_dbversion_t *source,
1324 1.1 christos dns_dbversion_t **targetp)
1325 1.1 christos {
1326 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
1327 1.1 christos rbtdb_version_t *rbtversion = source;
1328 1.1 christos
1329 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
1330 1.1 christos INSIST(rbtversion != NULL && rbtversion->rbtdb == rbtdb);
1331 1.1 christos
1332 1.3 christos isc_refcount_increment(&rbtversion->references);
1333 1.1 christos
1334 1.1 christos *targetp = rbtversion;
1335 1.1 christos }
1336 1.1 christos
1337 1.1 christos static rbtdb_changed_t *
1338 1.1 christos add_changed(dns_rbtdb_t *rbtdb, rbtdb_version_t *version,
1339 1.1 christos dns_rbtnode_t *node)
1340 1.1 christos {
1341 1.1 christos rbtdb_changed_t *changed;
1342 1.1 christos
1343 1.1 christos /*
1344 1.1 christos * Caller must be holding the node lock if its reference must be
1345 1.1 christos * protected by the lock.
1346 1.1 christos */
1347 1.1 christos
1348 1.1 christos changed = isc_mem_get(rbtdb->common.mctx, sizeof(*changed));
1349 1.1 christos
1350 1.1 christos RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
1351 1.1 christos
1352 1.1 christos REQUIRE(version->writer);
1353 1.1 christos
1354 1.1 christos if (changed != NULL) {
1355 1.3 christos isc_refcount_increment(&node->references);
1356 1.1 christos changed->node = node;
1357 1.3 christos changed->dirty = false;
1358 1.1 christos ISC_LIST_INITANDAPPEND(version->changed_list, changed, link);
1359 1.1 christos } else
1360 1.3 christos version->commit_ok = false;
1361 1.1 christos
1362 1.1 christos RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
1363 1.1 christos
1364 1.1 christos return (changed);
1365 1.1 christos }
1366 1.1 christos
1367 1.1 christos static inline void
1368 1.1 christos free_noqname(isc_mem_t *mctx, struct noqname **noqname) {
1369 1.1 christos
1370 1.1 christos if (dns_name_dynamic(&(*noqname)->name))
1371 1.1 christos dns_name_free(&(*noqname)->name, mctx);
1372 1.1 christos if ((*noqname)->neg != NULL)
1373 1.1 christos isc_mem_put(mctx, (*noqname)->neg,
1374 1.1 christos dns_rdataslab_size((*noqname)->neg, 0));
1375 1.1 christos if ((*noqname)->negsig != NULL)
1376 1.1 christos isc_mem_put(mctx, (*noqname)->negsig,
1377 1.1 christos dns_rdataslab_size((*noqname)->negsig, 0));
1378 1.1 christos isc_mem_put(mctx, *noqname, sizeof(**noqname));
1379 1.1 christos *noqname = NULL;
1380 1.1 christos }
1381 1.1 christos
1382 1.1 christos static inline void
1383 1.1 christos init_rdataset(dns_rbtdb_t *rbtdb, rdatasetheader_t *h) {
1384 1.1 christos ISC_LINK_INIT(h, link);
1385 1.1 christos h->heap_index = 0;
1386 1.1 christos h->is_mmapped = 0;
1387 1.1 christos h->next_is_relative = 0;
1388 1.1 christos h->node_is_relative = 0;
1389 1.1 christos
1390 1.1 christos #if TRACE_HEADER
1391 1.1 christos if (IS_CACHE(rbtdb) && rbtdb->common.rdclass == dns_rdataclass_in)
1392 1.1 christos fprintf(stderr, "initialized header: %p\n", h);
1393 1.1 christos #else
1394 1.1 christos UNUSED(rbtdb);
1395 1.1 christos #endif
1396 1.1 christos }
1397 1.1 christos
1398 1.1 christos /*
1399 1.1 christos * Update the copied values of 'next' and 'node' if they are relative.
1400 1.1 christos */
1401 1.1 christos static void
1402 1.1 christos update_newheader(rdatasetheader_t *newh, rdatasetheader_t *old) {
1403 1.1 christos char *p;
1404 1.1 christos
1405 1.1 christos if (old->next_is_relative) {
1406 1.1 christos p = (char *) old;
1407 1.1 christos p += (uintptr_t)old->next;
1408 1.1 christos newh->next = (rdatasetheader_t *)p;
1409 1.1 christos }
1410 1.1 christos if (old->node_is_relative) {
1411 1.1 christos p = (char *) old;
1412 1.1 christos p += (uintptr_t)old->node;
1413 1.1 christos newh->node = (dns_rbtnode_t *)p;
1414 1.1 christos }
1415 1.1 christos if (CASESET(old)) {
1416 1.3 christos uint16_t attr;
1417 1.1 christos
1418 1.1 christos memmove(newh->upper, old->upper, sizeof(old->upper));
1419 1.1 christos attr = old->attributes & (RDATASET_ATTR_CASESET |
1420 1.1 christos RDATASET_ATTR_CASEFULLYLOWER);
1421 1.1 christos newh->attributes |= attr;
1422 1.1 christos }
1423 1.1 christos }
1424 1.1 christos
1425 1.1 christos static inline rdatasetheader_t *
1426 1.1 christos new_rdataset(dns_rbtdb_t *rbtdb, isc_mem_t *mctx) {
1427 1.1 christos rdatasetheader_t *h;
1428 1.1 christos
1429 1.1 christos h = isc_mem_get(mctx, sizeof(*h));
1430 1.1 christos if (h == NULL)
1431 1.1 christos return (NULL);
1432 1.1 christos
1433 1.1 christos #if TRACE_HEADER
1434 1.1 christos if (IS_CACHE(rbtdb) && rbtdb->common.rdclass == dns_rdataclass_in)
1435 1.1 christos fprintf(stderr, "allocated header: %p\n", h);
1436 1.1 christos #endif
1437 1.1 christos memset(h->upper, 0xeb, sizeof(h->upper));
1438 1.1 christos init_rdataset(rbtdb, h);
1439 1.1 christos h->rdh_ttl = 0;
1440 1.1 christos return (h);
1441 1.1 christos }
1442 1.1 christos
1443 1.1 christos static inline void
1444 1.1 christos free_rdataset(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, rdatasetheader_t *rdataset) {
1445 1.1 christos unsigned int size;
1446 1.1 christos int idx;
1447 1.1 christos
1448 1.1 christos if (EXISTS(rdataset) &&
1449 1.1 christos (rdataset->attributes & RDATASET_ATTR_STATCOUNT) != 0) {
1450 1.3 christos update_rrsetstats(rbtdb, rdataset, false);
1451 1.1 christos }
1452 1.1 christos
1453 1.1 christos idx = rdataset->node->locknum;
1454 1.1 christos if (ISC_LINK_LINKED(rdataset, link)) {
1455 1.1 christos INSIST(IS_CACHE(rbtdb));
1456 1.1 christos ISC_LIST_UNLINK(rbtdb->rdatasets[idx], rdataset, link);
1457 1.1 christos }
1458 1.1 christos
1459 1.1 christos if (rdataset->heap_index != 0)
1460 1.1 christos isc_heap_delete(rbtdb->heaps[idx], rdataset->heap_index);
1461 1.1 christos rdataset->heap_index = 0;
1462 1.1 christos
1463 1.1 christos if (rdataset->noqname != NULL)
1464 1.1 christos free_noqname(mctx, &rdataset->noqname);
1465 1.1 christos if (rdataset->closest != NULL)
1466 1.1 christos free_noqname(mctx, &rdataset->closest);
1467 1.1 christos
1468 1.1 christos if (NONEXISTENT(rdataset))
1469 1.1 christos size = sizeof(*rdataset);
1470 1.1 christos else
1471 1.1 christos size = dns_rdataslab_size((unsigned char *)rdataset,
1472 1.1 christos sizeof(*rdataset));
1473 1.1 christos
1474 1.1 christos if (rdataset->is_mmapped == 1)
1475 1.1 christos return;
1476 1.1 christos
1477 1.1 christos isc_mem_put(mctx, rdataset, size);
1478 1.1 christos }
1479 1.1 christos
1480 1.1 christos static inline void
1481 1.1 christos rollback_node(dns_rbtnode_t *node, rbtdb_serial_t serial) {
1482 1.1 christos rdatasetheader_t *header, *dcurrent;
1483 1.3 christos bool make_dirty = false;
1484 1.1 christos
1485 1.1 christos /*
1486 1.1 christos * Caller must hold the node lock.
1487 1.1 christos */
1488 1.1 christos
1489 1.1 christos /*
1490 1.1 christos * We set the IGNORE attribute on rdatasets with serial number
1491 1.1 christos * 'serial'. When the reference count goes to zero, these rdatasets
1492 1.1 christos * will be cleaned up; until that time, they will be ignored.
1493 1.1 christos */
1494 1.1 christos for (header = node->data; header != NULL; header = header->next) {
1495 1.1 christos if (header->serial == serial) {
1496 1.1 christos header->attributes |= RDATASET_ATTR_IGNORE;
1497 1.3 christos make_dirty = true;
1498 1.1 christos }
1499 1.1 christos for (dcurrent = header->down;
1500 1.1 christos dcurrent != NULL;
1501 1.1 christos dcurrent = dcurrent->down) {
1502 1.1 christos if (dcurrent->serial == serial) {
1503 1.1 christos dcurrent->attributes |= RDATASET_ATTR_IGNORE;
1504 1.3 christos make_dirty = true;
1505 1.1 christos }
1506 1.1 christos }
1507 1.1 christos }
1508 1.1 christos if (make_dirty)
1509 1.1 christos node->dirty = 1;
1510 1.1 christos }
1511 1.1 christos
1512 1.1 christos static inline void
1513 1.1 christos mark_header_ancient(dns_rbtdb_t *rbtdb, rdatasetheader_t *header) {
1514 1.1 christos
1515 1.1 christos /*
1516 1.1 christos * If we are already ancient there is nothing to do.
1517 1.1 christos */
1518 1.1 christos if (ANCIENT(header))
1519 1.1 christos return;
1520 1.1 christos
1521 1.1 christos header->attributes |= RDATASET_ATTR_ANCIENT;
1522 1.1 christos header->node->dirty = 1;
1523 1.1 christos
1524 1.1 christos /*
1525 1.1 christos * If we have not been counted then there is nothing to do.
1526 1.1 christos */
1527 1.1 christos if ((header->attributes & RDATASET_ATTR_STATCOUNT) == 0)
1528 1.1 christos return;
1529 1.1 christos
1530 1.1 christos if (EXISTS(header))
1531 1.3 christos update_rrsetstats(rbtdb, header, true);
1532 1.1 christos }
1533 1.1 christos
1534 1.1 christos static inline void
1535 1.1 christos clean_stale_headers(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, rdatasetheader_t *top)
1536 1.1 christos {
1537 1.1 christos rdatasetheader_t *d, *down_next;
1538 1.1 christos
1539 1.1 christos for (d = top->down; d != NULL; d = down_next) {
1540 1.1 christos down_next = d->down;
1541 1.1 christos free_rdataset(rbtdb, mctx, d);
1542 1.1 christos }
1543 1.1 christos top->down = NULL;
1544 1.1 christos }
1545 1.1 christos
1546 1.1 christos static inline void
1547 1.1 christos clean_cache_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) {
1548 1.1 christos rdatasetheader_t *current, *top_prev, *top_next;
1549 1.1 christos isc_mem_t *mctx = rbtdb->common.mctx;
1550 1.1 christos
1551 1.1 christos /*
1552 1.1 christos * Caller must be holding the node lock.
1553 1.1 christos */
1554 1.1 christos
1555 1.1 christos top_prev = NULL;
1556 1.1 christos for (current = node->data; current != NULL; current = top_next) {
1557 1.1 christos top_next = current->next;
1558 1.1 christos clean_stale_headers(rbtdb, mctx, current);
1559 1.1 christos /*
1560 1.1 christos * If current is nonexistent or stale, we can clean it up.
1561 1.1 christos */
1562 1.1 christos if (NONEXISTENT(current) || ANCIENT(current) ||
1563 1.1 christos (STALE(current) && ! KEEPSTALE(rbtdb))) {
1564 1.1 christos if (top_prev != NULL)
1565 1.1 christos top_prev->next = current->next;
1566 1.1 christos else
1567 1.1 christos node->data = current->next;
1568 1.1 christos free_rdataset(rbtdb, mctx, current);
1569 1.1 christos } else
1570 1.1 christos top_prev = current;
1571 1.1 christos }
1572 1.1 christos node->dirty = 0;
1573 1.1 christos }
1574 1.1 christos
1575 1.1 christos static inline void
1576 1.1 christos clean_zone_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
1577 1.1 christos rbtdb_serial_t least_serial)
1578 1.1 christos {
1579 1.1 christos rdatasetheader_t *current, *dcurrent, *down_next, *dparent;
1580 1.1 christos rdatasetheader_t *top_prev, *top_next;
1581 1.1 christos isc_mem_t *mctx = rbtdb->common.mctx;
1582 1.3 christos bool still_dirty = false;
1583 1.1 christos
1584 1.1 christos /*
1585 1.1 christos * Caller must be holding the node lock.
1586 1.1 christos */
1587 1.1 christos REQUIRE(least_serial != 0);
1588 1.1 christos
1589 1.1 christos top_prev = NULL;
1590 1.1 christos for (current = node->data; current != NULL; current = top_next) {
1591 1.1 christos top_next = current->next;
1592 1.1 christos
1593 1.1 christos /*
1594 1.1 christos * First, we clean up any instances of multiple rdatasets
1595 1.1 christos * with the same serial number, or that have the IGNORE
1596 1.1 christos * attribute.
1597 1.1 christos */
1598 1.1 christos dparent = current;
1599 1.1 christos for (dcurrent = current->down;
1600 1.1 christos dcurrent != NULL;
1601 1.1 christos dcurrent = down_next) {
1602 1.1 christos down_next = dcurrent->down;
1603 1.1 christos INSIST(dcurrent->serial <= dparent->serial);
1604 1.1 christos if (dcurrent->serial == dparent->serial ||
1605 1.1 christos IGNORE(dcurrent)) {
1606 1.1 christos if (down_next != NULL)
1607 1.1 christos down_next->next = dparent;
1608 1.1 christos dparent->down = down_next;
1609 1.1 christos free_rdataset(rbtdb, mctx, dcurrent);
1610 1.1 christos } else
1611 1.1 christos dparent = dcurrent;
1612 1.1 christos }
1613 1.1 christos
1614 1.1 christos /*
1615 1.1 christos * We've now eliminated all IGNORE datasets with the possible
1616 1.1 christos * exception of current, which we now check.
1617 1.1 christos */
1618 1.1 christos if (IGNORE(current)) {
1619 1.1 christos down_next = current->down;
1620 1.1 christos if (down_next == NULL) {
1621 1.1 christos if (top_prev != NULL)
1622 1.1 christos top_prev->next = current->next;
1623 1.1 christos else
1624 1.1 christos node->data = current->next;
1625 1.1 christos free_rdataset(rbtdb, mctx, current);
1626 1.1 christos /*
1627 1.1 christos * current no longer exists, so we can
1628 1.1 christos * just continue with the loop.
1629 1.1 christos */
1630 1.1 christos continue;
1631 1.1 christos } else {
1632 1.1 christos /*
1633 1.1 christos * Pull up current->down, making it the new
1634 1.1 christos * current.
1635 1.1 christos */
1636 1.1 christos if (top_prev != NULL)
1637 1.1 christos top_prev->next = down_next;
1638 1.1 christos else
1639 1.1 christos node->data = down_next;
1640 1.1 christos down_next->next = top_next;
1641 1.1 christos free_rdataset(rbtdb, mctx, current);
1642 1.1 christos current = down_next;
1643 1.1 christos }
1644 1.1 christos }
1645 1.1 christos
1646 1.1 christos /*
1647 1.1 christos * We now try to find the first down node less than the
1648 1.1 christos * least serial.
1649 1.1 christos */
1650 1.1 christos dparent = current;
1651 1.1 christos for (dcurrent = current->down;
1652 1.1 christos dcurrent != NULL;
1653 1.1 christos dcurrent = down_next) {
1654 1.1 christos down_next = dcurrent->down;
1655 1.1 christos if (dcurrent->serial < least_serial)
1656 1.1 christos break;
1657 1.1 christos dparent = dcurrent;
1658 1.1 christos }
1659 1.1 christos
1660 1.1 christos /*
1661 1.1 christos * If there is a such an rdataset, delete it and any older
1662 1.1 christos * versions.
1663 1.1 christos */
1664 1.1 christos if (dcurrent != NULL) {
1665 1.1 christos do {
1666 1.1 christos down_next = dcurrent->down;
1667 1.1 christos INSIST(dcurrent->serial <= least_serial);
1668 1.1 christos free_rdataset(rbtdb, mctx, dcurrent);
1669 1.1 christos dcurrent = down_next;
1670 1.1 christos } while (dcurrent != NULL);
1671 1.1 christos dparent->down = NULL;
1672 1.1 christos }
1673 1.1 christos
1674 1.1 christos /*
1675 1.1 christos * Note. The serial number of 'current' might be less than
1676 1.1 christos * least_serial too, but we cannot delete it because it is
1677 1.1 christos * the most recent version, unless it is a NONEXISTENT
1678 1.1 christos * rdataset.
1679 1.1 christos */
1680 1.1 christos if (current->down != NULL) {
1681 1.3 christos still_dirty = true;
1682 1.1 christos top_prev = current;
1683 1.1 christos } else {
1684 1.1 christos /*
1685 1.1 christos * If this is a NONEXISTENT rdataset, we can delete it.
1686 1.1 christos */
1687 1.1 christos if (NONEXISTENT(current)) {
1688 1.1 christos if (top_prev != NULL)
1689 1.1 christos top_prev->next = current->next;
1690 1.1 christos else
1691 1.1 christos node->data = current->next;
1692 1.1 christos free_rdataset(rbtdb, mctx, current);
1693 1.1 christos } else
1694 1.1 christos top_prev = current;
1695 1.1 christos }
1696 1.1 christos }
1697 1.1 christos if (!still_dirty)
1698 1.1 christos node->dirty = 0;
1699 1.1 christos }
1700 1.1 christos
1701 1.1 christos static void
1702 1.1 christos delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) {
1703 1.1 christos dns_rbtnode_t *nsecnode;
1704 1.1 christos dns_fixedname_t fname;
1705 1.1 christos dns_name_t *name;
1706 1.1 christos isc_result_t result = ISC_R_UNEXPECTED;
1707 1.1 christos
1708 1.1 christos INSIST(!ISC_LINK_LINKED(node, deadlink));
1709 1.1 christos
1710 1.1 christos if (isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(1))) {
1711 1.1 christos char printname[DNS_NAME_FORMATSIZE];
1712 1.1 christos isc_log_write(dns_lctx,
1713 1.1 christos DNS_LOGCATEGORY_DATABASE,
1714 1.1 christos DNS_LOGMODULE_CACHE,
1715 1.1 christos ISC_LOG_DEBUG(1),
1716 1.1 christos "delete_node(): %p %s (bucket %d)",
1717 1.1 christos node,
1718 1.1 christos dns_rbt_formatnodename(node,
1719 1.1 christos printname, sizeof(printname)),
1720 1.1 christos node->locknum);
1721 1.1 christos }
1722 1.1 christos
1723 1.1 christos switch (node->nsec) {
1724 1.1 christos case DNS_RBT_NSEC_NORMAL:
1725 1.1 christos /*
1726 1.1 christos * Though this may be wasteful, it has to be done before
1727 1.1 christos * node is deleted.
1728 1.1 christos */
1729 1.1 christos name = dns_fixedname_initname(&fname);
1730 1.1 christos dns_rbt_fullnamefromnode(node, name);
1731 1.1 christos
1732 1.3 christos result = dns_rbt_deletenode(rbtdb->tree, node, false);
1733 1.1 christos break;
1734 1.1 christos case DNS_RBT_NSEC_HAS_NSEC:
1735 1.1 christos name = dns_fixedname_initname(&fname);
1736 1.1 christos dns_rbt_fullnamefromnode(node, name);
1737 1.1 christos /*
1738 1.1 christos * Delete the corresponding node from the auxiliary NSEC
1739 1.1 christos * tree before deleting from the main tree.
1740 1.1 christos */
1741 1.1 christos nsecnode = NULL;
1742 1.1 christos result = dns_rbt_findnode(rbtdb->nsec, name, NULL, &nsecnode,
1743 1.1 christos NULL, DNS_RBTFIND_EMPTYDATA,
1744 1.1 christos NULL, NULL);
1745 1.1 christos if (result != ISC_R_SUCCESS) {
1746 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1747 1.1 christos DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
1748 1.1 christos "delete_node: "
1749 1.1 christos "dns_rbt_findnode(nsec): %s",
1750 1.1 christos isc_result_totext(result));
1751 1.1 christos } else {
1752 1.1 christos result = dns_rbt_deletenode(rbtdb->nsec, nsecnode,
1753 1.3 christos false);
1754 1.1 christos if (result != ISC_R_SUCCESS) {
1755 1.1 christos isc_log_write(dns_lctx,
1756 1.1 christos DNS_LOGCATEGORY_DATABASE,
1757 1.1 christos DNS_LOGMODULE_CACHE,
1758 1.1 christos ISC_LOG_WARNING,
1759 1.1 christos "delete_node(): "
1760 1.1 christos "dns_rbt_deletenode(nsecnode): %s",
1761 1.1 christos isc_result_totext(result));
1762 1.1 christos }
1763 1.1 christos }
1764 1.3 christos result = dns_rbt_deletenode(rbtdb->tree, node, false);
1765 1.1 christos break;
1766 1.1 christos case DNS_RBT_NSEC_NSEC:
1767 1.3 christos result = dns_rbt_deletenode(rbtdb->nsec, node, false);
1768 1.1 christos break;
1769 1.1 christos case DNS_RBT_NSEC_NSEC3:
1770 1.3 christos result = dns_rbt_deletenode(rbtdb->nsec3, node, false);
1771 1.1 christos break;
1772 1.1 christos }
1773 1.1 christos if (result != ISC_R_SUCCESS) {
1774 1.1 christos isc_log_write(dns_lctx,
1775 1.1 christos DNS_LOGCATEGORY_DATABASE,
1776 1.1 christos DNS_LOGMODULE_CACHE,
1777 1.1 christos ISC_LOG_WARNING,
1778 1.1 christos "delete_node(): "
1779 1.1 christos "dns_rbt_deletenode: %s",
1780 1.1 christos isc_result_totext(result));
1781 1.1 christos }
1782 1.1 christos }
1783 1.1 christos
1784 1.1 christos /*
1785 1.1 christos * Caller must be holding the node lock.
1786 1.1 christos */
1787 1.1 christos static inline void
1788 1.1 christos new_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) {
1789 1.1 christos INSIST(!ISC_LINK_LINKED(node, deadlink));
1790 1.3 christos if (isc_refcount_increment(&node->references) == 0) {
1791 1.3 christos /* this is the first reference to the node */
1792 1.3 christos isc_refcount_increment0(&rbtdb->node_locks[node->locknum].references);
1793 1.1 christos }
1794 1.1 christos }
1795 1.1 christos
1796 1.1 christos /*%
1797 1.1 christos * Clean up dead nodes. These are nodes which have no references, and
1798 1.1 christos * have no data. They are dead but we could not or chose not to delete
1799 1.1 christos * them when we deleted all the data at that node because we did not want
1800 1.1 christos * to wait for the tree write lock.
1801 1.1 christos *
1802 1.1 christos * The caller must hold a tree write lock and bucketnum'th node (write) lock.
1803 1.1 christos */
1804 1.1 christos static void
1805 1.1 christos cleanup_dead_nodes(dns_rbtdb_t *rbtdb, int bucketnum) {
1806 1.1 christos dns_rbtnode_t *node;
1807 1.1 christos int count = 10; /* XXXJT: should be adjustable */
1808 1.1 christos
1809 1.1 christos node = ISC_LIST_HEAD(rbtdb->deadnodes[bucketnum]);
1810 1.1 christos while (node != NULL && count > 0) {
1811 1.1 christos ISC_LIST_UNLINK(rbtdb->deadnodes[bucketnum], node, deadlink);
1812 1.1 christos
1813 1.1 christos /*
1814 1.1 christos * Since we're holding a tree write lock, it should be
1815 1.1 christos * impossible for this node to be referenced by others.
1816 1.1 christos */
1817 1.3 christos INSIST(isc_refcount_current(&node->references) == 0 &&
1818 1.1 christos node->data == NULL);
1819 1.1 christos
1820 1.1 christos if (node->parent != NULL &&
1821 1.1 christos node->parent->down == node && node->left == NULL &&
1822 1.1 christos node->right == NULL && rbtdb->task != NULL)
1823 1.1 christos {
1824 1.1 christos isc_event_t *ev;
1825 1.1 christos dns_db_t *db;
1826 1.1 christos
1827 1.1 christos ev = isc_event_allocate(rbtdb->common.mctx, NULL,
1828 1.1 christos DNS_EVENT_RBTPRUNE,
1829 1.1 christos prune_tree, node,
1830 1.1 christos sizeof(isc_event_t));
1831 1.1 christos if (ev != NULL) {
1832 1.1 christos new_reference(rbtdb, node);
1833 1.1 christos db = NULL;
1834 1.1 christos attach((dns_db_t *)rbtdb, &db);
1835 1.1 christos ev->ev_sender = db;
1836 1.1 christos isc_task_send(rbtdb->task, &ev);
1837 1.1 christos } else {
1838 1.1 christos ISC_LIST_APPEND(rbtdb->deadnodes[bucketnum],
1839 1.1 christos node, deadlink);
1840 1.1 christos }
1841 1.1 christos } else {
1842 1.1 christos delete_node(rbtdb, node);
1843 1.1 christos }
1844 1.1 christos node = ISC_LIST_HEAD(rbtdb->deadnodes[bucketnum]);
1845 1.1 christos count--;
1846 1.1 christos }
1847 1.1 christos }
1848 1.1 christos
1849 1.1 christos /*
1850 1.1 christos * This function is assumed to be called when a node is newly referenced
1851 1.1 christos * and can be in the deadnode list. In that case the node must be retrieved
1852 1.1 christos * from the list because it is going to be used. In addition, if the caller
1853 1.1 christos * happens to hold a write lock on the tree, it's a good chance to purge dead
1854 1.1 christos * nodes.
1855 1.1 christos * Note: while a new reference is gained in multiple places, there are only very
1856 1.1 christos * few cases where the node can be in the deadnode list (only empty nodes can
1857 1.1 christos * have been added to the list).
1858 1.1 christos */
1859 1.1 christos static inline void
1860 1.1 christos reactivate_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
1861 1.1 christos isc_rwlocktype_t treelocktype)
1862 1.1 christos {
1863 1.1 christos isc_rwlocktype_t locktype = isc_rwlocktype_read;
1864 1.1 christos nodelock_t *nodelock = &rbtdb->node_locks[node->locknum].lock;
1865 1.3 christos bool maybe_cleanup = false;
1866 1.1 christos
1867 1.1 christos POST(locktype);
1868 1.1 christos
1869 1.3 christos NODE_LOCK(nodelock, locktype);
1870 1.1 christos
1871 1.1 christos /*
1872 1.1 christos * Check if we can possibly cleanup the dead node. If so, upgrade
1873 1.1 christos * the node lock below to perform the cleanup.
1874 1.1 christos */
1875 1.1 christos if (!ISC_LIST_EMPTY(rbtdb->deadnodes[node->locknum]) &&
1876 1.1 christos treelocktype == isc_rwlocktype_write) {
1877 1.3 christos maybe_cleanup = true;
1878 1.1 christos }
1879 1.1 christos
1880 1.1 christos if (ISC_LINK_LINKED(node, deadlink) || maybe_cleanup) {
1881 1.1 christos /*
1882 1.1 christos * Upgrade the lock and test if we still need to unlink.
1883 1.1 christos */
1884 1.3 christos NODE_UNLOCK(nodelock, locktype);
1885 1.1 christos locktype = isc_rwlocktype_write;
1886 1.1 christos POST(locktype);
1887 1.3 christos NODE_LOCK(nodelock, locktype);
1888 1.1 christos if (ISC_LINK_LINKED(node, deadlink))
1889 1.1 christos ISC_LIST_UNLINK(rbtdb->deadnodes[node->locknum],
1890 1.1 christos node, deadlink);
1891 1.1 christos if (maybe_cleanup)
1892 1.1 christos cleanup_dead_nodes(rbtdb, node->locknum);
1893 1.1 christos }
1894 1.1 christos
1895 1.1 christos new_reference(rbtdb, node);
1896 1.1 christos
1897 1.3 christos NODE_UNLOCK(nodelock, locktype);
1898 1.1 christos }
1899 1.1 christos
1900 1.1 christos /*
1901 1.1 christos * Caller must be holding the node lock; either the "strong", read or write
1902 1.1 christos * lock. Note that the lock must be held even when node references are
1903 1.1 christos * atomically modified; in that case the decrement operation itself does not
1904 1.1 christos * have to be protected, but we must avoid a race condition where multiple
1905 1.1 christos * threads are decreasing the reference to zero simultaneously and at least
1906 1.1 christos * one of them is going to free the node.
1907 1.1 christos *
1908 1.3 christos * This function returns true if and only if the node reference decreases
1909 1.1 christos * to zero.
1910 1.1 christos *
1911 1.1 christos * NOTE: Decrementing the reference count of a node to zero does not mean it
1912 1.1 christos * will be immediately freed.
1913 1.1 christos */
1914 1.3 christos static bool
1915 1.1 christos decrement_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
1916 1.1 christos rbtdb_serial_t least_serial,
1917 1.1 christos isc_rwlocktype_t nlock, isc_rwlocktype_t tlock,
1918 1.3 christos bool pruning)
1919 1.1 christos {
1920 1.1 christos isc_result_t result;
1921 1.3 christos bool write_locked;
1922 1.1 christos rbtdb_nodelock_t *nodelock;
1923 1.1 christos int bucket = node->locknum;
1924 1.3 christos bool no_reference = true;
1925 1.1 christos
1926 1.1 christos nodelock = &rbtdb->node_locks[bucket];
1927 1.1 christos
1928 1.1 christos #define KEEP_NODE(n, r) \
1929 1.1 christos ((n)->data != NULL || (n)->down != NULL || \
1930 1.1 christos (n) == (r)->origin_node || (n) == (r)->nsec3_origin_node)
1931 1.1 christos
1932 1.1 christos /* Handle easy and typical case first. */
1933 1.1 christos if (!node->dirty && KEEP_NODE(node, rbtdb)) {
1934 1.3 christos if (isc_refcount_decrement(&node->references) == 1) {
1935 1.3 christos isc_refcount_decrement(&nodelock->references);
1936 1.3 christos return (true);
1937 1.3 christos } else {
1938 1.3 christos return (false);
1939 1.1 christos }
1940 1.1 christos }
1941 1.1 christos
1942 1.1 christos /* Upgrade the lock? */
1943 1.1 christos if (nlock == isc_rwlocktype_read) {
1944 1.3 christos NODE_UNLOCK(&nodelock->lock, isc_rwlocktype_read);
1945 1.3 christos NODE_LOCK(&nodelock->lock, isc_rwlocktype_write);
1946 1.1 christos }
1947 1.1 christos
1948 1.3 christos if (isc_refcount_decrement(&node->references) > 1) {
1949 1.1 christos /* Restore the lock? */
1950 1.1 christos if (nlock == isc_rwlocktype_read)
1951 1.3 christos NODE_DOWNGRADE(&nodelock->lock);
1952 1.3 christos return (false);
1953 1.1 christos }
1954 1.1 christos
1955 1.1 christos if (node->dirty) {
1956 1.1 christos if (IS_CACHE(rbtdb))
1957 1.1 christos clean_cache_node(rbtdb, node);
1958 1.1 christos else {
1959 1.1 christos if (least_serial == 0) {
1960 1.1 christos /*
1961 1.1 christos * Caller doesn't know the least serial.
1962 1.1 christos * Get it.
1963 1.1 christos */
1964 1.1 christos RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read);
1965 1.1 christos least_serial = rbtdb->least_serial;
1966 1.1 christos RBTDB_UNLOCK(&rbtdb->lock,
1967 1.1 christos isc_rwlocktype_read);
1968 1.1 christos }
1969 1.1 christos clean_zone_node(rbtdb, node, least_serial);
1970 1.1 christos }
1971 1.1 christos }
1972 1.1 christos
1973 1.1 christos /*
1974 1.1 christos * Attempt to switch to a write lock on the tree. If this fails,
1975 1.1 christos * we will add this node to a linked list of nodes in this locking
1976 1.1 christos * bucket which we will free later.
1977 1.1 christos */
1978 1.1 christos if (tlock != isc_rwlocktype_write) {
1979 1.1 christos /*
1980 1.1 christos * Locking hierarchy notwithstanding, we don't need to free
1981 1.1 christos * the node lock before acquiring the tree write lock because
1982 1.1 christos * we only do a trylock.
1983 1.1 christos */
1984 1.1 christos if (tlock == isc_rwlocktype_read)
1985 1.1 christos result = isc_rwlock_tryupgrade(&rbtdb->tree_lock);
1986 1.1 christos else
1987 1.1 christos result = isc_rwlock_trylock(&rbtdb->tree_lock,
1988 1.1 christos isc_rwlocktype_write);
1989 1.1 christos RUNTIME_CHECK(result == ISC_R_SUCCESS ||
1990 1.1 christos result == ISC_R_LOCKBUSY);
1991 1.1 christos
1992 1.3 christos write_locked = (result == ISC_R_SUCCESS);
1993 1.1 christos } else
1994 1.3 christos write_locked = true;
1995 1.1 christos
1996 1.3 christos INSIST(isc_refcount_decrement(&nodelock->references) > 0);
1997 1.1 christos
1998 1.1 christos if (KEEP_NODE(node, rbtdb))
1999 1.1 christos goto restore_locks;
2000 1.1 christos
2001 1.1 christos #undef KEEP_NODE
2002 1.1 christos
2003 1.1 christos if (write_locked) {
2004 1.1 christos /*
2005 1.1 christos * We can now delete the node.
2006 1.1 christos */
2007 1.1 christos
2008 1.1 christos /*
2009 1.1 christos * If this node is the only one in the level it's in, deleting
2010 1.1 christos * this node may recursively make its parent the only node in
2011 1.1 christos * the parent level; if so, and if no one is currently using
2012 1.1 christos * the parent node, this is almost the only opportunity to
2013 1.1 christos * clean it up. But the recursive cleanup is not that trivial
2014 1.1 christos * since the child and parent may be in different lock buckets,
2015 1.1 christos * which would cause a lock order reversal problem. To avoid
2016 1.1 christos * the trouble, we'll dispatch a separate event for batch
2017 1.1 christos * cleaning. We need to check whether we're deleting the node
2018 1.1 christos * as a result of pruning to avoid infinite dispatching.
2019 1.1 christos * Note: pruning happens only when a task has been set for the
2020 1.1 christos * rbtdb. If the user of the rbtdb chooses not to set a task,
2021 1.1 christos * it's their responsibility to purge stale leaves (e.g. by
2022 1.1 christos * periodic walk-through).
2023 1.1 christos */
2024 1.1 christos if (!pruning && node->parent != NULL &&
2025 1.1 christos node->parent->down == node && node->left == NULL &&
2026 1.1 christos node->right == NULL && rbtdb->task != NULL) {
2027 1.1 christos isc_event_t *ev;
2028 1.1 christos dns_db_t *db;
2029 1.1 christos
2030 1.1 christos ev = isc_event_allocate(rbtdb->common.mctx, NULL,
2031 1.1 christos DNS_EVENT_RBTPRUNE,
2032 1.1 christos prune_tree, node,
2033 1.1 christos sizeof(isc_event_t));
2034 1.1 christos if (ev != NULL) {
2035 1.1 christos new_reference(rbtdb, node);
2036 1.1 christos db = NULL;
2037 1.1 christos attach((dns_db_t *)rbtdb, &db);
2038 1.1 christos ev->ev_sender = db;
2039 1.1 christos isc_task_send(rbtdb->task, &ev);
2040 1.3 christos no_reference = false;
2041 1.1 christos } else {
2042 1.1 christos /*
2043 1.1 christos * XXX: this is a weird situation. We could
2044 1.1 christos * ignore this error case, but then the stale
2045 1.1 christos * node will unlikely be purged except via a
2046 1.1 christos * rare condition such as manual cleanup. So
2047 1.1 christos * we queue it in the deadnodes list, hoping
2048 1.1 christos * the memory shortage is temporary and the node
2049 1.1 christos * will be deleted later.
2050 1.1 christos */
2051 1.1 christos isc_log_write(dns_lctx,
2052 1.1 christos DNS_LOGCATEGORY_DATABASE,
2053 1.1 christos DNS_LOGMODULE_CACHE,
2054 1.1 christos ISC_LOG_INFO,
2055 1.1 christos "decrement_reference: failed to "
2056 1.1 christos "allocate pruning event");
2057 1.1 christos INSIST(node->data == NULL);
2058 1.1 christos INSIST(!ISC_LINK_LINKED(node, deadlink));
2059 1.1 christos ISC_LIST_APPEND(rbtdb->deadnodes[bucket], node,
2060 1.1 christos deadlink);
2061 1.1 christos }
2062 1.1 christos } else {
2063 1.1 christos delete_node(rbtdb, node);
2064 1.1 christos }
2065 1.1 christos } else {
2066 1.1 christos INSIST(node->data == NULL);
2067 1.1 christos INSIST(!ISC_LINK_LINKED(node, deadlink));
2068 1.1 christos ISC_LIST_APPEND(rbtdb->deadnodes[bucket], node, deadlink);
2069 1.1 christos }
2070 1.1 christos
2071 1.1 christos restore_locks:
2072 1.1 christos /* Restore the lock? */
2073 1.1 christos if (nlock == isc_rwlocktype_read)
2074 1.3 christos NODE_DOWNGRADE(&nodelock->lock);
2075 1.1 christos
2076 1.1 christos /*
2077 1.1 christos * Relock a read lock, or unlock the write lock if no lock was held.
2078 1.1 christos */
2079 1.1 christos if (tlock == isc_rwlocktype_none)
2080 1.1 christos if (write_locked)
2081 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
2082 1.1 christos
2083 1.1 christos if (tlock == isc_rwlocktype_read)
2084 1.1 christos if (write_locked)
2085 1.1 christos isc_rwlock_downgrade(&rbtdb->tree_lock);
2086 1.1 christos
2087 1.1 christos return (no_reference);
2088 1.1 christos }
2089 1.1 christos
2090 1.1 christos /*
2091 1.1 christos * Prune the tree by recursively cleaning-up single leaves. In the worst
2092 1.1 christos * case, the number of iteration is the number of tree levels, which is at
2093 1.1 christos * most the maximum number of domain name labels, i.e, 127. In practice, this
2094 1.1 christos * should be much smaller (only a few times), and even the worst case would be
2095 1.1 christos * acceptable for a single event.
2096 1.1 christos */
2097 1.1 christos static void
2098 1.1 christos prune_tree(isc_task_t *task, isc_event_t *event) {
2099 1.1 christos dns_rbtdb_t *rbtdb = event->ev_sender;
2100 1.1 christos dns_rbtnode_t *node = event->ev_arg;
2101 1.1 christos dns_rbtnode_t *parent;
2102 1.1 christos unsigned int locknum;
2103 1.1 christos
2104 1.1 christos UNUSED(task);
2105 1.1 christos
2106 1.1 christos isc_event_free(&event);
2107 1.1 christos
2108 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
2109 1.1 christos locknum = node->locknum;
2110 1.1 christos NODE_LOCK(&rbtdb->node_locks[locknum].lock, isc_rwlocktype_write);
2111 1.1 christos do {
2112 1.1 christos parent = node->parent;
2113 1.1 christos decrement_reference(rbtdb, node, 0, isc_rwlocktype_write,
2114 1.3 christos isc_rwlocktype_write, true);
2115 1.1 christos
2116 1.1 christos if (parent != NULL && parent->down == NULL) {
2117 1.1 christos /*
2118 1.1 christos * node was the only down child of the parent and has
2119 1.1 christos * just been removed. We'll then need to examine the
2120 1.1 christos * parent. Keep the lock if possible; otherwise,
2121 1.1 christos * release the old lock and acquire one for the parent.
2122 1.1 christos */
2123 1.1 christos if (parent->locknum != locknum) {
2124 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
2125 1.1 christos isc_rwlocktype_write);
2126 1.1 christos locknum = parent->locknum;
2127 1.1 christos NODE_LOCK(&rbtdb->node_locks[locknum].lock,
2128 1.1 christos isc_rwlocktype_write);
2129 1.1 christos }
2130 1.1 christos
2131 1.1 christos /*
2132 1.1 christos * We need to gain a reference to the node before
2133 1.1 christos * decrementing it in the next iteration. In addition,
2134 1.1 christos * if the node is in the dead-nodes list, extract it
2135 1.1 christos * from the list beforehand as we do in
2136 1.1 christos * reactivate_node().
2137 1.1 christos */
2138 1.1 christos if (ISC_LINK_LINKED(parent, deadlink))
2139 1.1 christos ISC_LIST_UNLINK(rbtdb->deadnodes[locknum],
2140 1.1 christos parent, deadlink);
2141 1.1 christos new_reference(rbtdb, parent);
2142 1.1 christos } else
2143 1.1 christos parent = NULL;
2144 1.1 christos
2145 1.1 christos node = parent;
2146 1.1 christos } while (node != NULL);
2147 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, isc_rwlocktype_write);
2148 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
2149 1.1 christos
2150 1.2 christos detach((dns_db_t **)(void *)&rbtdb);
2151 1.1 christos }
2152 1.1 christos
2153 1.1 christos static inline void
2154 1.1 christos make_least_version(dns_rbtdb_t *rbtdb, rbtdb_version_t *version,
2155 1.1 christos rbtdb_changedlist_t *cleanup_list)
2156 1.1 christos {
2157 1.1 christos /*
2158 1.1 christos * Caller must be holding the database lock.
2159 1.1 christos */
2160 1.1 christos
2161 1.1 christos rbtdb->least_serial = version->serial;
2162 1.1 christos *cleanup_list = version->changed_list;
2163 1.1 christos ISC_LIST_INIT(version->changed_list);
2164 1.1 christos }
2165 1.1 christos
2166 1.1 christos static inline void
2167 1.1 christos cleanup_nondirty(rbtdb_version_t *version, rbtdb_changedlist_t *cleanup_list) {
2168 1.1 christos rbtdb_changed_t *changed, *next_changed;
2169 1.1 christos
2170 1.1 christos /*
2171 1.1 christos * If the changed record is dirty, then
2172 1.1 christos * an update created multiple versions of
2173 1.1 christos * a given rdataset. We keep this list
2174 1.1 christos * until we're the least open version, at
2175 1.1 christos * which point it's safe to get rid of any
2176 1.1 christos * older versions.
2177 1.1 christos *
2178 1.1 christos * If the changed record isn't dirty, then
2179 1.1 christos * we don't need it anymore since we're
2180 1.1 christos * committing and not rolling back.
2181 1.1 christos *
2182 1.1 christos * The caller must be holding the database lock.
2183 1.1 christos */
2184 1.1 christos for (changed = HEAD(version->changed_list);
2185 1.1 christos changed != NULL;
2186 1.1 christos changed = next_changed) {
2187 1.1 christos next_changed = NEXT(changed, link);
2188 1.1 christos if (!changed->dirty) {
2189 1.1 christos UNLINK(version->changed_list,
2190 1.1 christos changed, link);
2191 1.1 christos APPEND(*cleanup_list,
2192 1.1 christos changed, link);
2193 1.1 christos }
2194 1.1 christos }
2195 1.1 christos }
2196 1.1 christos
2197 1.1 christos static void
2198 1.1 christos iszonesecure(dns_db_t *db, rbtdb_version_t *version, dns_dbnode_t *origin) {
2199 1.1 christos dns_rdataset_t keyset;
2200 1.1 christos dns_rdataset_t nsecset, signsecset;
2201 1.3 christos bool haszonekey = false;
2202 1.3 christos bool hasnsec = false;
2203 1.1 christos isc_result_t result;
2204 1.1 christos
2205 1.1 christos dns_rdataset_init(&keyset);
2206 1.1 christos result = dns_db_findrdataset(db, origin, version, dns_rdatatype_dnskey,
2207 1.1 christos 0, 0, &keyset, NULL);
2208 1.1 christos if (result == ISC_R_SUCCESS) {
2209 1.1 christos result = dns_rdataset_first(&keyset);
2210 1.1 christos while (result == ISC_R_SUCCESS) {
2211 1.1 christos dns_rdata_t keyrdata = DNS_RDATA_INIT;
2212 1.1 christos dns_rdataset_current(&keyset, &keyrdata);
2213 1.1 christos if (dns_zonekey_iszonekey(&keyrdata)) {
2214 1.3 christos haszonekey = true;
2215 1.1 christos break;
2216 1.1 christos }
2217 1.1 christos result = dns_rdataset_next(&keyset);
2218 1.1 christos }
2219 1.1 christos dns_rdataset_disassociate(&keyset);
2220 1.1 christos }
2221 1.1 christos if (!haszonekey) {
2222 1.1 christos version->secure = dns_db_insecure;
2223 1.3 christos version->havensec3 = false;
2224 1.1 christos return;
2225 1.1 christos }
2226 1.1 christos
2227 1.1 christos dns_rdataset_init(&nsecset);
2228 1.1 christos dns_rdataset_init(&signsecset);
2229 1.1 christos result = dns_db_findrdataset(db, origin, version, dns_rdatatype_nsec,
2230 1.1 christos 0, 0, &nsecset, &signsecset);
2231 1.1 christos if (result == ISC_R_SUCCESS) {
2232 1.1 christos if (dns_rdataset_isassociated(&signsecset)) {
2233 1.3 christos hasnsec = true;
2234 1.1 christos dns_rdataset_disassociate(&signsecset);
2235 1.1 christos }
2236 1.1 christos dns_rdataset_disassociate(&nsecset);
2237 1.1 christos }
2238 1.1 christos
2239 1.1 christos setnsec3parameters(db, version);
2240 1.1 christos
2241 1.1 christos /*
2242 1.1 christos * Do we have a valid NSEC/NSEC3 chain?
2243 1.1 christos */
2244 1.1 christos if (version->havensec3 || hasnsec)
2245 1.1 christos version->secure = dns_db_secure;
2246 1.1 christos else
2247 1.1 christos version->secure = dns_db_insecure;
2248 1.1 christos }
2249 1.1 christos
2250 1.1 christos /*%<
2251 1.1 christos * Walk the origin node looking for NSEC3PARAM records.
2252 1.1 christos * Cache the nsec3 parameters.
2253 1.1 christos */
2254 1.1 christos static void
2255 1.1 christos setnsec3parameters(dns_db_t *db, rbtdb_version_t *version) {
2256 1.1 christos dns_rbtnode_t *node;
2257 1.1 christos dns_rdata_nsec3param_t nsec3param;
2258 1.1 christos dns_rdata_t rdata = DNS_RDATA_INIT;
2259 1.1 christos isc_region_t region;
2260 1.1 christos isc_result_t result;
2261 1.1 christos rdatasetheader_t *header, *header_next;
2262 1.1 christos unsigned char *raw; /* RDATASLAB */
2263 1.1 christos unsigned int count, length;
2264 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
2265 1.1 christos
2266 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
2267 1.3 christos version->havensec3 = false;
2268 1.1 christos node = rbtdb->origin_node;
2269 1.1 christos NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
2270 1.1 christos isc_rwlocktype_read);
2271 1.1 christos for (header = node->data;
2272 1.1 christos header != NULL;
2273 1.1 christos header = header_next) {
2274 1.1 christos header_next = header->next;
2275 1.1 christos do {
2276 1.1 christos if (header->serial <= version->serial &&
2277 1.1 christos !IGNORE(header)) {
2278 1.1 christos if (NONEXISTENT(header))
2279 1.1 christos header = NULL;
2280 1.1 christos break;
2281 1.1 christos } else
2282 1.1 christos header = header->down;
2283 1.1 christos } while (header != NULL);
2284 1.1 christos
2285 1.1 christos if (header != NULL &&
2286 1.1 christos (header->type == dns_rdatatype_nsec3param)) {
2287 1.1 christos /*
2288 1.1 christos * Find A NSEC3PARAM with a supported algorithm.
2289 1.1 christos */
2290 1.1 christos raw = (unsigned char *)header + sizeof(*header);
2291 1.1 christos count = raw[0] * 256 + raw[1]; /* count */
2292 1.1 christos #if DNS_RDATASET_FIXED
2293 1.1 christos raw += count * 4 + 2;
2294 1.1 christos #else
2295 1.1 christos raw += 2;
2296 1.1 christos #endif
2297 1.1 christos while (count-- > 0U) {
2298 1.1 christos length = raw[0] * 256 + raw[1];
2299 1.1 christos #if DNS_RDATASET_FIXED
2300 1.1 christos raw += 4;
2301 1.1 christos #else
2302 1.1 christos raw += 2;
2303 1.1 christos #endif
2304 1.1 christos region.base = raw;
2305 1.1 christos region.length = length;
2306 1.1 christos raw += length;
2307 1.1 christos dns_rdata_fromregion(&rdata,
2308 1.1 christos rbtdb->common.rdclass,
2309 1.1 christos dns_rdatatype_nsec3param,
2310 1.1 christos ®ion);
2311 1.1 christos result = dns_rdata_tostruct(&rdata,
2312 1.1 christos &nsec3param,
2313 1.1 christos NULL);
2314 1.1 christos INSIST(result == ISC_R_SUCCESS);
2315 1.1 christos dns_rdata_reset(&rdata);
2316 1.1 christos
2317 1.1 christos if (nsec3param.hash != DNS_NSEC3_UNKNOWNALG &&
2318 1.1 christos !dns_nsec3_supportedhash(nsec3param.hash))
2319 1.1 christos continue;
2320 1.1 christos
2321 1.1 christos if (nsec3param.flags != 0)
2322 1.1 christos continue;
2323 1.1 christos
2324 1.1 christos memmove(version->salt, nsec3param.salt,
2325 1.1 christos nsec3param.salt_length);
2326 1.1 christos version->hash = nsec3param.hash;
2327 1.1 christos version->salt_length = nsec3param.salt_length;
2328 1.1 christos version->iterations = nsec3param.iterations;
2329 1.1 christos version->flags = nsec3param.flags;
2330 1.3 christos version->havensec3 = true;
2331 1.1 christos /*
2332 1.1 christos * Look for a better algorithm than the
2333 1.1 christos * unknown test algorithm.
2334 1.1 christos */
2335 1.1 christos if (nsec3param.hash != DNS_NSEC3_UNKNOWNALG)
2336 1.1 christos goto unlock;
2337 1.1 christos }
2338 1.1 christos }
2339 1.1 christos }
2340 1.1 christos unlock:
2341 1.1 christos NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
2342 1.1 christos isc_rwlocktype_read);
2343 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
2344 1.1 christos }
2345 1.1 christos
2346 1.1 christos static void
2347 1.1 christos cleanup_dead_nodes_callback(isc_task_t *task, isc_event_t *event) {
2348 1.1 christos dns_rbtdb_t *rbtdb = event->ev_arg;
2349 1.3 christos bool again = false;
2350 1.1 christos unsigned int locknum;
2351 1.1 christos
2352 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
2353 1.1 christos for (locknum = 0; locknum < rbtdb->node_lock_count; locknum++) {
2354 1.1 christos NODE_LOCK(&rbtdb->node_locks[locknum].lock,
2355 1.1 christos isc_rwlocktype_write);
2356 1.1 christos cleanup_dead_nodes(rbtdb, locknum);
2357 1.1 christos if (ISC_LIST_HEAD(rbtdb->deadnodes[locknum]) != NULL)
2358 1.3 christos again = true;
2359 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
2360 1.1 christos isc_rwlocktype_write);
2361 1.1 christos }
2362 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
2363 1.1 christos if (again)
2364 1.1 christos isc_task_send(task, &event);
2365 1.1 christos else {
2366 1.1 christos isc_event_free(&event);
2367 1.3 christos if (isc_refcount_decrement(&rbtdb->references) == 1) {
2368 1.3 christos (void)isc_refcount_current(&rbtdb->references);
2369 1.1 christos maybe_free_rbtdb(rbtdb);
2370 1.3 christos }
2371 1.1 christos }
2372 1.1 christos }
2373 1.1 christos
2374 1.1 christos static void
2375 1.3 christos closeversion(dns_db_t *db, dns_dbversion_t **versionp, bool commit) {
2376 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
2377 1.1 christos rbtdb_version_t *version, *cleanup_version, *least_greater;
2378 1.3 christos bool rollback = false;
2379 1.1 christos rbtdb_changedlist_t cleanup_list;
2380 1.1 christos rdatasetheaderlist_t resigned_list;
2381 1.1 christos rbtdb_changed_t *changed, *next_changed;
2382 1.1 christos rbtdb_serial_t serial, least_serial;
2383 1.1 christos dns_rbtnode_t *rbtnode;
2384 1.1 christos rdatasetheader_t *header;
2385 1.1 christos
2386 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
2387 1.1 christos version = (rbtdb_version_t *)*versionp;
2388 1.1 christos INSIST(version->rbtdb == rbtdb);
2389 1.1 christos
2390 1.1 christos cleanup_version = NULL;
2391 1.1 christos ISC_LIST_INIT(cleanup_list);
2392 1.1 christos ISC_LIST_INIT(resigned_list);
2393 1.1 christos
2394 1.3 christos if (isc_refcount_decrement(&version->references) > 1) {
2395 1.3 christos /* typical and easy case first */
2396 1.1 christos if (commit) {
2397 1.1 christos RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read);
2398 1.1 christos INSIST(!version->writer);
2399 1.1 christos RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read);
2400 1.1 christos }
2401 1.1 christos goto end;
2402 1.1 christos }
2403 1.1 christos
2404 1.1 christos /*
2405 1.1 christos * Update the zone's secure status in version before making
2406 1.1 christos * it the current version.
2407 1.1 christos */
2408 1.1 christos if (version->writer && commit && !IS_CACHE(rbtdb))
2409 1.1 christos iszonesecure(db, version, rbtdb->origin_node);
2410 1.1 christos
2411 1.1 christos RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
2412 1.1 christos serial = version->serial;
2413 1.1 christos if (version->writer) {
2414 1.1 christos if (commit) {
2415 1.1 christos unsigned cur_ref;
2416 1.1 christos rbtdb_version_t *cur_version;
2417 1.1 christos
2418 1.1 christos INSIST(version->commit_ok);
2419 1.1 christos INSIST(version == rbtdb->future_version);
2420 1.1 christos /*
2421 1.1 christos * The current version is going to be replaced.
2422 1.1 christos * Release the (likely last) reference to it from the
2423 1.1 christos * DB itself and unlink it from the open list.
2424 1.1 christos */
2425 1.1 christos cur_version = rbtdb->current_version;
2426 1.3 christos cur_ref = isc_refcount_decrement(&cur_version->references);
2427 1.3 christos if (cur_ref == 1) {
2428 1.3 christos (void)isc_refcount_current(&cur_version->references);
2429 1.1 christos if (cur_version->serial == rbtdb->least_serial)
2430 1.1 christos INSIST(EMPTY(cur_version->changed_list));
2431 1.1 christos UNLINK(rbtdb->open_versions,
2432 1.1 christos cur_version, link);
2433 1.1 christos }
2434 1.1 christos if (EMPTY(rbtdb->open_versions)) {
2435 1.1 christos /*
2436 1.1 christos * We're going to become the least open
2437 1.1 christos * version.
2438 1.1 christos */
2439 1.1 christos make_least_version(rbtdb, version,
2440 1.1 christos &cleanup_list);
2441 1.1 christos } else {
2442 1.1 christos /*
2443 1.1 christos * Some other open version is the
2444 1.1 christos * least version. We can't cleanup
2445 1.1 christos * records that were changed in this
2446 1.1 christos * version because the older versions
2447 1.1 christos * may still be in use by an open
2448 1.1 christos * version.
2449 1.1 christos *
2450 1.1 christos * We can, however, discard the
2451 1.1 christos * changed records for things that
2452 1.1 christos * we've added that didn't exist in
2453 1.1 christos * prior versions.
2454 1.1 christos */
2455 1.1 christos cleanup_nondirty(version, &cleanup_list);
2456 1.1 christos }
2457 1.1 christos /*
2458 1.1 christos * If the (soon to be former) current version
2459 1.1 christos * isn't being used by anyone, we can clean
2460 1.1 christos * it up.
2461 1.1 christos */
2462 1.3 christos if (cur_ref == 1) {
2463 1.1 christos cleanup_version = cur_version;
2464 1.1 christos APPENDLIST(version->changed_list,
2465 1.1 christos cleanup_version->changed_list,
2466 1.1 christos link);
2467 1.1 christos }
2468 1.1 christos /*
2469 1.1 christos * Become the current version.
2470 1.1 christos */
2471 1.3 christos version->writer = false;
2472 1.1 christos rbtdb->current_version = version;
2473 1.1 christos rbtdb->current_serial = version->serial;
2474 1.1 christos rbtdb->future_version = NULL;
2475 1.1 christos
2476 1.1 christos /*
2477 1.1 christos * Keep the current version in the open list, and
2478 1.1 christos * gain a reference for the DB itself (see the DB
2479 1.1 christos * creation function below). This must be the only
2480 1.1 christos * case where we need to increment the counter from
2481 1.1 christos * zero and need to use isc_refcount_increment0().
2482 1.1 christos */
2483 1.3 christos INSIST(isc_refcount_increment0(&version->references) == 0);
2484 1.1 christos PREPEND(rbtdb->open_versions,
2485 1.1 christos rbtdb->current_version, link);
2486 1.1 christos resigned_list = version->resigned_list;
2487 1.1 christos ISC_LIST_INIT(version->resigned_list);
2488 1.1 christos } else {
2489 1.1 christos /*
2490 1.1 christos * We're rolling back this transaction.
2491 1.1 christos */
2492 1.1 christos cleanup_list = version->changed_list;
2493 1.1 christos ISC_LIST_INIT(version->changed_list);
2494 1.1 christos resigned_list = version->resigned_list;
2495 1.1 christos ISC_LIST_INIT(version->resigned_list);
2496 1.3 christos rollback = true;
2497 1.1 christos cleanup_version = version;
2498 1.1 christos rbtdb->future_version = NULL;
2499 1.1 christos }
2500 1.1 christos } else {
2501 1.1 christos if (version != rbtdb->current_version) {
2502 1.1 christos /*
2503 1.1 christos * There are no external or internal references
2504 1.1 christos * to this version and it can be cleaned up.
2505 1.1 christos */
2506 1.1 christos cleanup_version = version;
2507 1.1 christos
2508 1.1 christos /*
2509 1.1 christos * Find the version with the least serial
2510 1.1 christos * number greater than ours.
2511 1.1 christos */
2512 1.1 christos least_greater = PREV(version, link);
2513 1.1 christos if (least_greater == NULL)
2514 1.1 christos least_greater = rbtdb->current_version;
2515 1.1 christos
2516 1.1 christos INSIST(version->serial < least_greater->serial);
2517 1.1 christos /*
2518 1.1 christos * Is this the least open version?
2519 1.1 christos */
2520 1.1 christos if (version->serial == rbtdb->least_serial) {
2521 1.1 christos /*
2522 1.1 christos * Yes. Install the new least open
2523 1.1 christos * version.
2524 1.1 christos */
2525 1.1 christos make_least_version(rbtdb,
2526 1.1 christos least_greater,
2527 1.1 christos &cleanup_list);
2528 1.1 christos } else {
2529 1.1 christos /*
2530 1.1 christos * Add any unexecuted cleanups to
2531 1.1 christos * those of the least greater version.
2532 1.1 christos */
2533 1.1 christos APPENDLIST(least_greater->changed_list,
2534 1.1 christos version->changed_list,
2535 1.1 christos link);
2536 1.1 christos }
2537 1.1 christos } else if (version->serial == rbtdb->least_serial)
2538 1.1 christos INSIST(EMPTY(version->changed_list));
2539 1.1 christos UNLINK(rbtdb->open_versions, version, link);
2540 1.1 christos }
2541 1.1 christos least_serial = rbtdb->least_serial;
2542 1.1 christos RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
2543 1.1 christos
2544 1.1 christos if (cleanup_version != NULL) {
2545 1.1 christos INSIST(EMPTY(cleanup_version->changed_list));
2546 1.1 christos free_gluetable(cleanup_version);
2547 1.1 christos isc_rwlock_destroy(&cleanup_version->glue_rwlock);
2548 1.1 christos isc_rwlock_destroy(&cleanup_version->rwlock);
2549 1.1 christos isc_mem_put(rbtdb->common.mctx, cleanup_version,
2550 1.1 christos sizeof(*cleanup_version));
2551 1.1 christos }
2552 1.1 christos
2553 1.1 christos /*
2554 1.1 christos * Commit/rollback re-signed headers.
2555 1.1 christos */
2556 1.1 christos for (header = HEAD(resigned_list);
2557 1.1 christos header != NULL;
2558 1.1 christos header = HEAD(resigned_list)) {
2559 1.1 christos nodelock_t *lock;
2560 1.1 christos
2561 1.1 christos ISC_LIST_UNLINK(resigned_list, header, link);
2562 1.1 christos
2563 1.1 christos lock = &rbtdb->node_locks[header->node->locknum].lock;
2564 1.1 christos NODE_LOCK(lock, isc_rwlocktype_write);
2565 1.1 christos if (rollback && !IGNORE(header)) {
2566 1.1 christos isc_result_t result;
2567 1.1 christos result = resign_insert(rbtdb, header->node->locknum,
2568 1.1 christos header);
2569 1.1 christos if (result != ISC_R_SUCCESS)
2570 1.1 christos isc_log_write(dns_lctx,
2571 1.1 christos DNS_LOGCATEGORY_DATABASE,
2572 1.1 christos DNS_LOGMODULE_ZONE, ISC_LOG_ERROR,
2573 1.1 christos "Unable to reinsert header to "
2574 1.1 christos "re-signing heap: %s",
2575 1.1 christos dns_result_totext(result));
2576 1.1 christos }
2577 1.1 christos decrement_reference(rbtdb, header->node, least_serial,
2578 1.1 christos isc_rwlocktype_write, isc_rwlocktype_none,
2579 1.3 christos false);
2580 1.1 christos NODE_UNLOCK(lock, isc_rwlocktype_write);
2581 1.1 christos }
2582 1.1 christos
2583 1.1 christos if (!EMPTY(cleanup_list)) {
2584 1.1 christos isc_event_t *event = NULL;
2585 1.1 christos isc_rwlocktype_t tlock = isc_rwlocktype_none;
2586 1.1 christos
2587 1.1 christos if (rbtdb->task != NULL)
2588 1.1 christos event = isc_event_allocate(rbtdb->common.mctx, NULL,
2589 1.1 christos DNS_EVENT_RBTDEADNODES,
2590 1.1 christos cleanup_dead_nodes_callback,
2591 1.1 christos rbtdb, sizeof(isc_event_t));
2592 1.1 christos if (event == NULL) {
2593 1.1 christos /*
2594 1.1 christos * We acquire a tree write lock here in order to make
2595 1.1 christos * sure that stale nodes will be removed in
2596 1.1 christos * decrement_reference(). If we didn't have the lock,
2597 1.1 christos * those nodes could miss the chance to be removed
2598 1.1 christos * until the server stops. The write lock is
2599 1.1 christos * expensive, but this event should be rare enough
2600 1.1 christos * to justify the cost.
2601 1.1 christos */
2602 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
2603 1.1 christos tlock = isc_rwlocktype_write;
2604 1.1 christos }
2605 1.1 christos
2606 1.1 christos for (changed = HEAD(cleanup_list);
2607 1.1 christos changed != NULL;
2608 1.1 christos changed = next_changed) {
2609 1.1 christos nodelock_t *lock;
2610 1.1 christos
2611 1.1 christos next_changed = NEXT(changed, link);
2612 1.1 christos rbtnode = changed->node;
2613 1.1 christos lock = &rbtdb->node_locks[rbtnode->locknum].lock;
2614 1.1 christos
2615 1.1 christos NODE_LOCK(lock, isc_rwlocktype_write);
2616 1.1 christos /*
2617 1.1 christos * This is a good opportunity to purge any dead nodes,
2618 1.1 christos * so use it.
2619 1.1 christos */
2620 1.1 christos if (event == NULL)
2621 1.1 christos cleanup_dead_nodes(rbtdb, rbtnode->locknum);
2622 1.1 christos
2623 1.1 christos if (rollback)
2624 1.1 christos rollback_node(rbtnode, serial);
2625 1.1 christos decrement_reference(rbtdb, rbtnode, least_serial,
2626 1.1 christos isc_rwlocktype_write, tlock,
2627 1.3 christos false);
2628 1.1 christos
2629 1.1 christos NODE_UNLOCK(lock, isc_rwlocktype_write);
2630 1.1 christos
2631 1.1 christos isc_mem_put(rbtdb->common.mctx, changed,
2632 1.1 christos sizeof(*changed));
2633 1.1 christos }
2634 1.1 christos if (event != NULL) {
2635 1.3 christos isc_refcount_increment(&rbtdb->references);
2636 1.1 christos isc_task_send(rbtdb->task, &event);
2637 1.1 christos } else
2638 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
2639 1.1 christos }
2640 1.1 christos
2641 1.1 christos end:
2642 1.1 christos *versionp = NULL;
2643 1.1 christos }
2644 1.1 christos
2645 1.1 christos /*
2646 1.1 christos * Add the necessary magic for the wildcard name 'name'
2647 1.1 christos * to be found in 'rbtdb'.
2648 1.1 christos *
2649 1.1 christos * In order for wildcard matching to work correctly in
2650 1.1 christos * zone_find(), we must ensure that a node for the wildcarding
2651 1.1 christos * level exists in the database, and has its 'find_callback'
2652 1.1 christos * and 'wild' bits set.
2653 1.1 christos *
2654 1.1 christos * E.g. if the wildcard name is "*.sub.example." then we
2655 1.1 christos * must ensure that "sub.example." exists and is marked as
2656 1.1 christos * a wildcard level.
2657 1.1 christos */
2658 1.1 christos static isc_result_t
2659 1.1 christos add_wildcard_magic(dns_rbtdb_t *rbtdb, const dns_name_t *name) {
2660 1.1 christos isc_result_t result;
2661 1.1 christos dns_name_t foundname;
2662 1.1 christos dns_offsets_t offsets;
2663 1.1 christos unsigned int n;
2664 1.1 christos dns_rbtnode_t *node = NULL;
2665 1.1 christos
2666 1.1 christos dns_name_init(&foundname, offsets);
2667 1.1 christos n = dns_name_countlabels(name);
2668 1.1 christos INSIST(n >= 2);
2669 1.1 christos n--;
2670 1.1 christos dns_name_getlabelsequence(name, 1, n, &foundname);
2671 1.1 christos result = dns_rbt_addnode(rbtdb->tree, &foundname, &node);
2672 1.1 christos if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS)
2673 1.1 christos return (result);
2674 1.1 christos if (result == ISC_R_SUCCESS)
2675 1.1 christos node->nsec = DNS_RBT_NSEC_NORMAL;
2676 1.1 christos node->find_callback = 1;
2677 1.1 christos node->wild = 1;
2678 1.1 christos return (ISC_R_SUCCESS);
2679 1.1 christos }
2680 1.1 christos
2681 1.1 christos static isc_result_t
2682 1.1 christos add_empty_wildcards(dns_rbtdb_t *rbtdb, const dns_name_t *name) {
2683 1.1 christos isc_result_t result;
2684 1.1 christos dns_name_t foundname;
2685 1.1 christos dns_offsets_t offsets;
2686 1.1 christos unsigned int n, l, i;
2687 1.1 christos
2688 1.1 christos dns_name_init(&foundname, offsets);
2689 1.1 christos n = dns_name_countlabels(name);
2690 1.1 christos l = dns_name_countlabels(&rbtdb->common.origin);
2691 1.1 christos i = l + 1;
2692 1.1 christos while (i < n) {
2693 1.1 christos dns_rbtnode_t *node = NULL; /* dummy */
2694 1.1 christos dns_name_getlabelsequence(name, n - i, i, &foundname);
2695 1.1 christos if (dns_name_iswildcard(&foundname)) {
2696 1.1 christos result = add_wildcard_magic(rbtdb, &foundname);
2697 1.1 christos if (result != ISC_R_SUCCESS)
2698 1.1 christos return (result);
2699 1.1 christos result = dns_rbt_addnode(rbtdb->tree, &foundname,
2700 1.1 christos &node);
2701 1.1 christos if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS)
2702 1.1 christos return (result);
2703 1.1 christos if (result == ISC_R_SUCCESS)
2704 1.1 christos node->nsec = DNS_RBT_NSEC_NORMAL;
2705 1.1 christos }
2706 1.1 christos i++;
2707 1.1 christos }
2708 1.1 christos return (ISC_R_SUCCESS);
2709 1.1 christos }
2710 1.1 christos
2711 1.1 christos static isc_result_t
2712 1.1 christos findnodeintree(dns_rbtdb_t *rbtdb, dns_rbt_t *tree, const dns_name_t *name,
2713 1.3 christos bool create, dns_dbnode_t **nodep)
2714 1.1 christos {
2715 1.1 christos dns_rbtnode_t *node = NULL;
2716 1.1 christos dns_name_t nodename;
2717 1.1 christos isc_result_t result;
2718 1.1 christos isc_rwlocktype_t locktype = isc_rwlocktype_read;
2719 1.1 christos
2720 1.1 christos INSIST(tree == rbtdb->tree || tree == rbtdb->nsec3);
2721 1.1 christos
2722 1.1 christos dns_name_init(&nodename, NULL);
2723 1.1 christos RWLOCK(&rbtdb->tree_lock, locktype);
2724 1.1 christos result = dns_rbt_findnode(tree, name, NULL, &node, NULL,
2725 1.1 christos DNS_RBTFIND_EMPTYDATA, NULL, NULL);
2726 1.1 christos if (result != ISC_R_SUCCESS) {
2727 1.1 christos RWUNLOCK(&rbtdb->tree_lock, locktype);
2728 1.1 christos if (!create) {
2729 1.1 christos if (result == DNS_R_PARTIALMATCH)
2730 1.1 christos result = ISC_R_NOTFOUND;
2731 1.1 christos return (result);
2732 1.1 christos }
2733 1.1 christos /*
2734 1.1 christos * It would be nice to try to upgrade the lock instead of
2735 1.1 christos * unlocking then relocking.
2736 1.1 christos */
2737 1.1 christos locktype = isc_rwlocktype_write;
2738 1.1 christos RWLOCK(&rbtdb->tree_lock, locktype);
2739 1.1 christos node = NULL;
2740 1.1 christos result = dns_rbt_addnode(tree, name, &node);
2741 1.1 christos if (result == ISC_R_SUCCESS) {
2742 1.1 christos dns_rbt_namefromnode(node, &nodename);
2743 1.1 christos node->locknum = node->hashval % rbtdb->node_lock_count;
2744 1.1 christos if (tree == rbtdb->tree) {
2745 1.1 christos add_empty_wildcards(rbtdb, name);
2746 1.1 christos
2747 1.1 christos if (dns_name_iswildcard(name)) {
2748 1.3 christos result = add_wildcard_magic(rbtdb,
2749 1.3 christos name);
2750 1.1 christos if (result != ISC_R_SUCCESS) {
2751 1.3 christos RWUNLOCK(&rbtdb->tree_lock,
2752 1.3 christos locktype);
2753 1.1 christos return (result);
2754 1.1 christos }
2755 1.1 christos }
2756 1.1 christos }
2757 1.1 christos if (tree == rbtdb->nsec3)
2758 1.1 christos node->nsec = DNS_RBT_NSEC_NSEC3;
2759 1.1 christos } else if (result != ISC_R_EXISTS) {
2760 1.1 christos RWUNLOCK(&rbtdb->tree_lock, locktype);
2761 1.1 christos return (result);
2762 1.1 christos }
2763 1.1 christos }
2764 1.1 christos
2765 1.1 christos if (tree == rbtdb->nsec3)
2766 1.1 christos INSIST(node->nsec == DNS_RBT_NSEC_NSEC3);
2767 1.1 christos
2768 1.1 christos reactivate_node(rbtdb, node, locktype);
2769 1.1 christos
2770 1.1 christos RWUNLOCK(&rbtdb->tree_lock, locktype);
2771 1.1 christos
2772 1.1 christos *nodep = (dns_dbnode_t *)node;
2773 1.1 christos
2774 1.1 christos return (ISC_R_SUCCESS);
2775 1.1 christos }
2776 1.1 christos
2777 1.1 christos static isc_result_t
2778 1.3 christos findnode(dns_db_t *db, const dns_name_t *name, bool create,
2779 1.1 christos dns_dbnode_t **nodep)
2780 1.1 christos {
2781 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
2782 1.1 christos
2783 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
2784 1.1 christos
2785 1.1 christos return (findnodeintree(rbtdb, rbtdb->tree, name, create, nodep));
2786 1.1 christos }
2787 1.1 christos
2788 1.1 christos static isc_result_t
2789 1.3 christos findnsec3node(dns_db_t *db, const dns_name_t *name, bool create,
2790 1.1 christos dns_dbnode_t **nodep)
2791 1.1 christos {
2792 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
2793 1.1 christos
2794 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
2795 1.1 christos
2796 1.1 christos return (findnodeintree(rbtdb, rbtdb->nsec3, name, create, nodep));
2797 1.1 christos }
2798 1.1 christos
2799 1.1 christos static isc_result_t
2800 1.1 christos zone_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
2801 1.1 christos rbtdb_search_t *search = arg;
2802 1.1 christos rdatasetheader_t *header, *header_next;
2803 1.1 christos rdatasetheader_t *dname_header, *sigdname_header, *ns_header;
2804 1.1 christos rdatasetheader_t *found;
2805 1.1 christos isc_result_t result;
2806 1.1 christos dns_rbtnode_t *onode;
2807 1.1 christos
2808 1.1 christos /*
2809 1.1 christos * We only want to remember the topmost zone cut, since it's the one
2810 1.1 christos * that counts, so we'll just continue if we've already found a
2811 1.1 christos * zonecut.
2812 1.1 christos */
2813 1.1 christos if (search->zonecut != NULL)
2814 1.1 christos return (DNS_R_CONTINUE);
2815 1.1 christos
2816 1.1 christos found = NULL;
2817 1.1 christos result = DNS_R_CONTINUE;
2818 1.1 christos onode = search->rbtdb->origin_node;
2819 1.1 christos
2820 1.1 christos NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock),
2821 1.1 christos isc_rwlocktype_read);
2822 1.1 christos
2823 1.1 christos /*
2824 1.1 christos * Look for an NS or DNAME rdataset active in our version.
2825 1.1 christos */
2826 1.1 christos ns_header = NULL;
2827 1.1 christos dname_header = NULL;
2828 1.1 christos sigdname_header = NULL;
2829 1.1 christos for (header = node->data; header != NULL; header = header_next) {
2830 1.1 christos header_next = header->next;
2831 1.1 christos if (header->type == dns_rdatatype_ns ||
2832 1.1 christos header->type == dns_rdatatype_dname ||
2833 1.1 christos header->type == RBTDB_RDATATYPE_SIGDNAME) {
2834 1.1 christos do {
2835 1.1 christos if (header->serial <= search->serial &&
2836 1.1 christos !IGNORE(header)) {
2837 1.1 christos /*
2838 1.1 christos * Is this a "this rdataset doesn't
2839 1.1 christos * exist" record?
2840 1.1 christos */
2841 1.1 christos if (NONEXISTENT(header))
2842 1.1 christos header = NULL;
2843 1.1 christos break;
2844 1.1 christos } else
2845 1.1 christos header = header->down;
2846 1.1 christos } while (header != NULL);
2847 1.1 christos if (header != NULL) {
2848 1.1 christos if (header->type == dns_rdatatype_dname)
2849 1.1 christos dname_header = header;
2850 1.1 christos else if (header->type ==
2851 1.1 christos RBTDB_RDATATYPE_SIGDNAME)
2852 1.1 christos sigdname_header = header;
2853 1.1 christos else if (node != onode ||
2854 1.1 christos IS_STUB(search->rbtdb)) {
2855 1.1 christos /*
2856 1.1 christos * We've found an NS rdataset that
2857 1.1 christos * isn't at the origin node. We check
2858 1.1 christos * that they're not at the origin node,
2859 1.1 christos * because otherwise we'd erroneously
2860 1.1 christos * treat the zone top as if it were
2861 1.1 christos * a delegation.
2862 1.1 christos */
2863 1.1 christos ns_header = header;
2864 1.1 christos }
2865 1.1 christos }
2866 1.1 christos }
2867 1.1 christos }
2868 1.1 christos
2869 1.1 christos /*
2870 1.1 christos * Did we find anything?
2871 1.1 christos */
2872 1.1 christos if (!IS_CACHE(search->rbtdb) && !IS_STUB(search->rbtdb) &&
2873 1.1 christos ns_header != NULL) {
2874 1.1 christos /*
2875 1.1 christos * Note that NS has precedence over DNAME if both exist
2876 1.1 christos * in a zone. Otherwise DNAME take precedence over NS.
2877 1.1 christos */
2878 1.1 christos found = ns_header;
2879 1.1 christos search->zonecut_sigrdataset = NULL;
2880 1.1 christos } else if (dname_header != NULL) {
2881 1.1 christos found = dname_header;
2882 1.1 christos search->zonecut_sigrdataset = sigdname_header;
2883 1.1 christos } else if (ns_header != NULL) {
2884 1.1 christos found = ns_header;
2885 1.1 christos search->zonecut_sigrdataset = NULL;
2886 1.1 christos }
2887 1.1 christos
2888 1.1 christos if (found != NULL) {
2889 1.1 christos /*
2890 1.1 christos * We increment the reference count on node to ensure that
2891 1.1 christos * search->zonecut_rdataset will still be valid later.
2892 1.1 christos */
2893 1.1 christos new_reference(search->rbtdb, node);
2894 1.1 christos search->zonecut = node;
2895 1.1 christos search->zonecut_rdataset = found;
2896 1.3 christos search->need_cleanup = true;
2897 1.1 christos /*
2898 1.1 christos * Since we've found a zonecut, anything beneath it is
2899 1.1 christos * glue and is not subject to wildcard matching, so we
2900 1.1 christos * may clear search->wild.
2901 1.1 christos */
2902 1.3 christos search->wild = false;
2903 1.1 christos if ((search->options & DNS_DBFIND_GLUEOK) == 0) {
2904 1.1 christos /*
2905 1.1 christos * If the caller does not want to find glue, then
2906 1.1 christos * this is the best answer and the search should
2907 1.1 christos * stop now.
2908 1.1 christos */
2909 1.1 christos result = DNS_R_PARTIALMATCH;
2910 1.1 christos } else {
2911 1.1 christos dns_name_t *zcname;
2912 1.1 christos
2913 1.1 christos /*
2914 1.1 christos * The search will continue beneath the zone cut.
2915 1.1 christos * This may or may not be the best match. In case it
2916 1.1 christos * is, we need to remember the node name.
2917 1.1 christos */
2918 1.1 christos zcname = dns_fixedname_name(&search->zonecut_name);
2919 1.1 christos RUNTIME_CHECK(dns_name_copy(name, zcname, NULL) ==
2920 1.1 christos ISC_R_SUCCESS);
2921 1.3 christos search->copy_name = true;
2922 1.1 christos }
2923 1.1 christos } else {
2924 1.1 christos /*
2925 1.1 christos * There is no zonecut at this node which is active in this
2926 1.1 christos * version.
2927 1.1 christos *
2928 1.1 christos * If this is a "wild" node and the caller hasn't disabled
2929 1.1 christos * wildcard matching, remember that we've seen a wild node
2930 1.1 christos * in case we need to go searching for wildcard matches
2931 1.1 christos * later on.
2932 1.1 christos */
2933 1.1 christos if (node->wild && (search->options & DNS_DBFIND_NOWILD) == 0)
2934 1.3 christos search->wild = true;
2935 1.1 christos }
2936 1.1 christos
2937 1.1 christos NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
2938 1.1 christos isc_rwlocktype_read);
2939 1.1 christos
2940 1.1 christos return (result);
2941 1.1 christos }
2942 1.1 christos
2943 1.1 christos static inline void
2944 1.1 christos bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
2945 1.1 christos rdatasetheader_t *header, isc_stdtime_t now,
2946 1.1 christos dns_rdataset_t *rdataset)
2947 1.1 christos {
2948 1.1 christos unsigned char *raw; /* RDATASLAB */
2949 1.1 christos
2950 1.1 christos /*
2951 1.1 christos * Caller must be holding the node reader lock.
2952 1.1 christos * XXXJT: technically, we need a writer lock, since we'll increment
2953 1.1 christos * the header count below. However, since the actual counter value
2954 1.1 christos * doesn't matter, we prioritize performance here. (We may want to
2955 1.1 christos * use atomic increment when available).
2956 1.1 christos */
2957 1.1 christos
2958 1.1 christos if (rdataset == NULL)
2959 1.1 christos return;
2960 1.1 christos
2961 1.1 christos new_reference(rbtdb, node);
2962 1.1 christos
2963 1.1 christos INSIST(rdataset->methods == NULL); /* We must be disassociated. */
2964 1.1 christos
2965 1.1 christos rdataset->methods = &rdataset_methods;
2966 1.1 christos rdataset->rdclass = rbtdb->common.rdclass;
2967 1.1 christos rdataset->type = RBTDB_RDATATYPE_BASE(header->type);
2968 1.1 christos rdataset->covers = RBTDB_RDATATYPE_EXT(header->type);
2969 1.1 christos rdataset->ttl = header->rdh_ttl - now;
2970 1.1 christos rdataset->trust = header->trust;
2971 1.1 christos if (NEGATIVE(header))
2972 1.1 christos rdataset->attributes |= DNS_RDATASETATTR_NEGATIVE;
2973 1.1 christos if (NXDOMAIN(header))
2974 1.1 christos rdataset->attributes |= DNS_RDATASETATTR_NXDOMAIN;
2975 1.1 christos if (OPTOUT(header))
2976 1.1 christos rdataset->attributes |= DNS_RDATASETATTR_OPTOUT;
2977 1.1 christos if (PREFETCH(header))
2978 1.1 christos rdataset->attributes |= DNS_RDATASETATTR_PREFETCH;
2979 1.1 christos if (STALE(header)) {
2980 1.1 christos rdataset->attributes |= DNS_RDATASETATTR_STALE;
2981 1.1 christos rdataset->ttl = 0;
2982 1.1 christos }
2983 1.1 christos rdataset->private1 = rbtdb;
2984 1.1 christos rdataset->private2 = node;
2985 1.1 christos raw = (unsigned char *)header + sizeof(*header);
2986 1.1 christos rdataset->private3 = raw;
2987 1.1 christos rdataset->count = header->count++;
2988 1.3 christos if (rdataset->count == UINT32_MAX)
2989 1.1 christos rdataset->count = 0;
2990 1.1 christos
2991 1.1 christos /*
2992 1.1 christos * Reset iterator state.
2993 1.1 christos */
2994 1.1 christos rdataset->privateuint4 = 0;
2995 1.1 christos rdataset->private5 = NULL;
2996 1.1 christos
2997 1.1 christos /*
2998 1.1 christos * Add noqname proof.
2999 1.1 christos */
3000 1.1 christos rdataset->private6 = header->noqname;
3001 1.1 christos if (rdataset->private6 != NULL)
3002 1.1 christos rdataset->attributes |= DNS_RDATASETATTR_NOQNAME;
3003 1.1 christos rdataset->private7 = header->closest;
3004 1.1 christos if (rdataset->private7 != NULL)
3005 1.1 christos rdataset->attributes |= DNS_RDATASETATTR_CLOSEST;
3006 1.1 christos
3007 1.1 christos /*
3008 1.1 christos * Copy out re-signing information.
3009 1.1 christos */
3010 1.1 christos if (RESIGN(header)) {
3011 1.1 christos rdataset->attributes |= DNS_RDATASETATTR_RESIGN;
3012 1.1 christos rdataset->resign = (header->resign << 1) | header->resign_lsb;
3013 1.1 christos } else
3014 1.1 christos rdataset->resign = 0;
3015 1.1 christos }
3016 1.1 christos
3017 1.1 christos static inline isc_result_t
3018 1.1 christos setup_delegation(rbtdb_search_t *search, dns_dbnode_t **nodep,
3019 1.1 christos dns_name_t *foundname, dns_rdataset_t *rdataset,
3020 1.1 christos dns_rdataset_t *sigrdataset)
3021 1.1 christos {
3022 1.1 christos isc_result_t result;
3023 1.1 christos dns_name_t *zcname;
3024 1.1 christos rbtdb_rdatatype_t type;
3025 1.1 christos dns_rbtnode_t *node;
3026 1.1 christos
3027 1.1 christos /*
3028 1.1 christos * The caller MUST NOT be holding any node locks.
3029 1.1 christos */
3030 1.1 christos
3031 1.1 christos node = search->zonecut;
3032 1.1 christos type = search->zonecut_rdataset->type;
3033 1.1 christos
3034 1.1 christos /*
3035 1.1 christos * If we have to set foundname, we do it before anything else.
3036 1.1 christos * If we were to set foundname after we had set nodep or bound the
3037 1.1 christos * rdataset, then we'd have to undo that work if dns_name_copy()
3038 1.1 christos * failed. By setting foundname first, there's nothing to undo if
3039 1.1 christos * we have trouble.
3040 1.1 christos */
3041 1.1 christos if (foundname != NULL && search->copy_name) {
3042 1.1 christos zcname = dns_fixedname_name(&search->zonecut_name);
3043 1.1 christos result = dns_name_copy(zcname, foundname, NULL);
3044 1.1 christos if (result != ISC_R_SUCCESS)
3045 1.1 christos return (result);
3046 1.1 christos }
3047 1.1 christos if (nodep != NULL) {
3048 1.1 christos /*
3049 1.1 christos * Note that we don't have to increment the node's reference
3050 1.1 christos * count here because we're going to use the reference we
3051 1.1 christos * already have in the search block.
3052 1.1 christos */
3053 1.1 christos *nodep = node;
3054 1.3 christos search->need_cleanup = false;
3055 1.1 christos }
3056 1.1 christos if (rdataset != NULL) {
3057 1.1 christos NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock),
3058 1.1 christos isc_rwlocktype_read);
3059 1.1 christos bind_rdataset(search->rbtdb, node, search->zonecut_rdataset,
3060 1.1 christos search->now, rdataset);
3061 1.1 christos if (sigrdataset != NULL && search->zonecut_sigrdataset != NULL)
3062 1.1 christos bind_rdataset(search->rbtdb, node,
3063 1.1 christos search->zonecut_sigrdataset,
3064 1.1 christos search->now, sigrdataset);
3065 1.1 christos NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
3066 1.1 christos isc_rwlocktype_read);
3067 1.1 christos }
3068 1.1 christos
3069 1.1 christos if (type == dns_rdatatype_dname)
3070 1.1 christos return (DNS_R_DNAME);
3071 1.1 christos return (DNS_R_DELEGATION);
3072 1.1 christos }
3073 1.1 christos
3074 1.3 christos static inline bool
3075 1.1 christos valid_glue(rbtdb_search_t *search, dns_name_t *name, rbtdb_rdatatype_t type,
3076 1.1 christos dns_rbtnode_t *node)
3077 1.1 christos {
3078 1.1 christos unsigned char *raw; /* RDATASLAB */
3079 1.1 christos unsigned int count, size;
3080 1.1 christos dns_name_t ns_name;
3081 1.3 christos bool valid = false;
3082 1.1 christos dns_offsets_t offsets;
3083 1.1 christos isc_region_t region;
3084 1.1 christos rdatasetheader_t *header;
3085 1.1 christos
3086 1.1 christos /*
3087 1.1 christos * No additional locking is required.
3088 1.1 christos */
3089 1.1 christos
3090 1.1 christos /*
3091 1.1 christos * Valid glue types are A, AAAA, A6. NS is also a valid glue type
3092 1.1 christos * if it occurs at a zone cut, but is not valid below it.
3093 1.1 christos */
3094 1.1 christos if (type == dns_rdatatype_ns) {
3095 1.1 christos if (node != search->zonecut) {
3096 1.3 christos return (false);
3097 1.1 christos }
3098 1.1 christos } else if (type != dns_rdatatype_a &&
3099 1.1 christos type != dns_rdatatype_aaaa &&
3100 1.1 christos type != dns_rdatatype_a6) {
3101 1.3 christos return (false);
3102 1.1 christos }
3103 1.1 christos
3104 1.1 christos header = search->zonecut_rdataset;
3105 1.1 christos raw = (unsigned char *)header + sizeof(*header);
3106 1.1 christos count = raw[0] * 256 + raw[1];
3107 1.1 christos #if DNS_RDATASET_FIXED
3108 1.1 christos raw += 2 + (4 * count);
3109 1.1 christos #else
3110 1.1 christos raw += 2;
3111 1.1 christos #endif
3112 1.1 christos
3113 1.1 christos while (count > 0) {
3114 1.1 christos count--;
3115 1.1 christos size = raw[0] * 256 + raw[1];
3116 1.1 christos #if DNS_RDATASET_FIXED
3117 1.1 christos raw += 4;
3118 1.1 christos #else
3119 1.1 christos raw += 2;
3120 1.1 christos #endif
3121 1.1 christos region.base = raw;
3122 1.1 christos region.length = size;
3123 1.1 christos raw += size;
3124 1.1 christos /*
3125 1.1 christos * XXX Until we have rdata structures, we have no choice but
3126 1.1 christos * to directly access the rdata format.
3127 1.1 christos */
3128 1.1 christos dns_name_init(&ns_name, offsets);
3129 1.1 christos dns_name_fromregion(&ns_name, ®ion);
3130 1.1 christos if (dns_name_compare(&ns_name, name) == 0) {
3131 1.3 christos valid = true;
3132 1.1 christos break;
3133 1.1 christos }
3134 1.1 christos }
3135 1.1 christos
3136 1.1 christos return (valid);
3137 1.1 christos }
3138 1.1 christos
3139 1.3 christos static inline bool
3140 1.1 christos activeempty(rbtdb_search_t *search, dns_rbtnodechain_t *chain,
3141 1.1 christos const dns_name_t *name)
3142 1.1 christos {
3143 1.1 christos dns_fixedname_t fnext;
3144 1.1 christos dns_fixedname_t forigin;
3145 1.1 christos dns_name_t *next;
3146 1.1 christos dns_name_t *origin;
3147 1.1 christos dns_name_t prefix;
3148 1.1 christos dns_rbtdb_t *rbtdb;
3149 1.1 christos dns_rbtnode_t *node;
3150 1.1 christos isc_result_t result;
3151 1.3 christos bool answer = false;
3152 1.1 christos rdatasetheader_t *header;
3153 1.1 christos
3154 1.1 christos rbtdb = search->rbtdb;
3155 1.1 christos
3156 1.1 christos dns_name_init(&prefix, NULL);
3157 1.1 christos next = dns_fixedname_initname(&fnext);
3158 1.1 christos origin = dns_fixedname_initname(&forigin);
3159 1.1 christos
3160 1.1 christos result = dns_rbtnodechain_next(chain, NULL, NULL);
3161 1.1 christos while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
3162 1.1 christos node = NULL;
3163 1.1 christos result = dns_rbtnodechain_current(chain, &prefix,
3164 1.1 christos origin, &node);
3165 1.1 christos if (result != ISC_R_SUCCESS)
3166 1.1 christos break;
3167 1.1 christos NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
3168 1.1 christos isc_rwlocktype_read);
3169 1.1 christos for (header = node->data;
3170 1.1 christos header != NULL;
3171 1.1 christos header = header->next) {
3172 1.1 christos if (header->serial <= search->serial &&
3173 1.1 christos !IGNORE(header) && EXISTS(header))
3174 1.1 christos break;
3175 1.1 christos }
3176 1.1 christos NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
3177 1.1 christos isc_rwlocktype_read);
3178 1.1 christos if (header != NULL)
3179 1.1 christos break;
3180 1.1 christos result = dns_rbtnodechain_next(chain, NULL, NULL);
3181 1.1 christos }
3182 1.1 christos if (result == ISC_R_SUCCESS)
3183 1.1 christos result = dns_name_concatenate(&prefix, origin, next, NULL);
3184 1.1 christos if (result == ISC_R_SUCCESS && dns_name_issubdomain(next, name))
3185 1.3 christos answer = true;
3186 1.1 christos return (answer);
3187 1.1 christos }
3188 1.1 christos
3189 1.3 christos static inline bool
3190 1.1 christos activeemtpynode(rbtdb_search_t *search, const dns_name_t *qname,
3191 1.1 christos dns_name_t *wname)
3192 1.1 christos {
3193 1.1 christos dns_fixedname_t fnext;
3194 1.1 christos dns_fixedname_t forigin;
3195 1.1 christos dns_fixedname_t fprev;
3196 1.1 christos dns_name_t *next;
3197 1.1 christos dns_name_t *origin;
3198 1.1 christos dns_name_t *prev;
3199 1.1 christos dns_name_t name;
3200 1.1 christos dns_name_t rname;
3201 1.1 christos dns_name_t tname;
3202 1.1 christos dns_rbtdb_t *rbtdb;
3203 1.1 christos dns_rbtnode_t *node;
3204 1.1 christos dns_rbtnodechain_t chain;
3205 1.3 christos bool check_next = true;
3206 1.3 christos bool check_prev = true;
3207 1.3 christos bool answer = false;
3208 1.1 christos isc_result_t result;
3209 1.1 christos rdatasetheader_t *header;
3210 1.1 christos unsigned int n;
3211 1.1 christos
3212 1.1 christos rbtdb = search->rbtdb;
3213 1.1 christos
3214 1.1 christos dns_name_init(&name, NULL);
3215 1.1 christos dns_name_init(&tname, NULL);
3216 1.1 christos dns_name_init(&rname, NULL);
3217 1.1 christos next = dns_fixedname_initname(&fnext);
3218 1.1 christos prev = dns_fixedname_initname(&fprev);
3219 1.1 christos origin = dns_fixedname_initname(&forigin);
3220 1.1 christos
3221 1.1 christos /*
3222 1.1 christos * Find if qname is at or below a empty node.
3223 1.1 christos * Use our own copy of the chain.
3224 1.1 christos */
3225 1.1 christos
3226 1.1 christos chain = search->chain;
3227 1.1 christos do {
3228 1.1 christos node = NULL;
3229 1.1 christos result = dns_rbtnodechain_current(&chain, &name,
3230 1.1 christos origin, &node);
3231 1.1 christos if (result != ISC_R_SUCCESS)
3232 1.1 christos break;
3233 1.1 christos NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
3234 1.1 christos isc_rwlocktype_read);
3235 1.1 christos for (header = node->data;
3236 1.1 christos header != NULL;
3237 1.1 christos header = header->next) {
3238 1.1 christos if (header->serial <= search->serial &&
3239 1.1 christos !IGNORE(header) && EXISTS(header))
3240 1.1 christos break;
3241 1.1 christos }
3242 1.1 christos NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
3243 1.1 christos isc_rwlocktype_read);
3244 1.1 christos if (header != NULL)
3245 1.1 christos break;
3246 1.1 christos result = dns_rbtnodechain_prev(&chain, NULL, NULL);
3247 1.1 christos } while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN);
3248 1.1 christos if (result == ISC_R_SUCCESS)
3249 1.1 christos result = dns_name_concatenate(&name, origin, prev, NULL);
3250 1.1 christos if (result != ISC_R_SUCCESS)
3251 1.3 christos check_prev = false;
3252 1.1 christos
3253 1.1 christos result = dns_rbtnodechain_next(&chain, NULL, NULL);
3254 1.1 christos while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
3255 1.1 christos node = NULL;
3256 1.1 christos result = dns_rbtnodechain_current(&chain, &name,
3257 1.1 christos origin, &node);
3258 1.1 christos if (result != ISC_R_SUCCESS)
3259 1.1 christos break;
3260 1.1 christos NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
3261 1.1 christos isc_rwlocktype_read);
3262 1.1 christos for (header = node->data;
3263 1.1 christos header != NULL;
3264 1.1 christos header = header->next) {
3265 1.1 christos if (header->serial <= search->serial &&
3266 1.1 christos !IGNORE(header) && EXISTS(header))
3267 1.1 christos break;
3268 1.1 christos }
3269 1.1 christos NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
3270 1.1 christos isc_rwlocktype_read);
3271 1.1 christos if (header != NULL)
3272 1.1 christos break;
3273 1.1 christos result = dns_rbtnodechain_next(&chain, NULL, NULL);
3274 1.1 christos }
3275 1.1 christos if (result == ISC_R_SUCCESS)
3276 1.1 christos result = dns_name_concatenate(&name, origin, next, NULL);
3277 1.1 christos if (result != ISC_R_SUCCESS)
3278 1.3 christos check_next = false;
3279 1.1 christos
3280 1.1 christos dns_name_clone(qname, &rname);
3281 1.1 christos
3282 1.1 christos /*
3283 1.1 christos * Remove the wildcard label to find the terminal name.
3284 1.1 christos */
3285 1.1 christos n = dns_name_countlabels(wname);
3286 1.1 christos dns_name_getlabelsequence(wname, 1, n - 1, &tname);
3287 1.1 christos
3288 1.1 christos do {
3289 1.1 christos if ((check_prev && dns_name_issubdomain(prev, &rname)) ||
3290 1.1 christos (check_next && dns_name_issubdomain(next, &rname))) {
3291 1.3 christos answer = true;
3292 1.1 christos break;
3293 1.1 christos }
3294 1.1 christos /*
3295 1.1 christos * Remove the left hand label.
3296 1.1 christos */
3297 1.1 christos n = dns_name_countlabels(&rname);
3298 1.1 christos dns_name_getlabelsequence(&rname, 1, n - 1, &rname);
3299 1.1 christos } while (!dns_name_equal(&rname, &tname));
3300 1.1 christos return (answer);
3301 1.1 christos }
3302 1.1 christos
3303 1.1 christos static inline isc_result_t
3304 1.1 christos find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep,
3305 1.1 christos const dns_name_t *qname)
3306 1.1 christos {
3307 1.1 christos unsigned int i, j;
3308 1.1 christos dns_rbtnode_t *node, *level_node, *wnode;
3309 1.1 christos rdatasetheader_t *header;
3310 1.1 christos isc_result_t result = ISC_R_NOTFOUND;
3311 1.1 christos dns_name_t name;
3312 1.1 christos dns_name_t *wname;
3313 1.1 christos dns_fixedname_t fwname;
3314 1.1 christos dns_rbtdb_t *rbtdb;
3315 1.3 christos bool done, wild, active;
3316 1.1 christos dns_rbtnodechain_t wchain;
3317 1.1 christos
3318 1.1 christos /*
3319 1.1 christos * Caller must be holding the tree lock and MUST NOT be holding
3320 1.1 christos * any node locks.
3321 1.1 christos */
3322 1.1 christos
3323 1.1 christos /*
3324 1.1 christos * Examine each ancestor level. If the level's wild bit
3325 1.1 christos * is set, then construct the corresponding wildcard name and
3326 1.1 christos * search for it. If the wildcard node exists, and is active in
3327 1.1 christos * this version, we're done. If not, then we next check to see
3328 1.1 christos * if the ancestor is active in this version. If so, then there
3329 1.1 christos * can be no possible wildcard match and again we're done. If not,
3330 1.1 christos * continue the search.
3331 1.1 christos */
3332 1.1 christos
3333 1.1 christos rbtdb = search->rbtdb;
3334 1.1 christos i = search->chain.level_matches;
3335 1.3 christos done = false;
3336 1.1 christos node = *nodep;
3337 1.1 christos do {
3338 1.1 christos NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
3339 1.1 christos isc_rwlocktype_read);
3340 1.1 christos
3341 1.1 christos /*
3342 1.1 christos * First we try to figure out if this node is active in
3343 1.1 christos * the search's version. We do this now, even though we
3344 1.1 christos * may not need the information, because it simplifies the
3345 1.1 christos * locking and code flow.
3346 1.1 christos */
3347 1.1 christos for (header = node->data;
3348 1.1 christos header != NULL;
3349 1.1 christos header = header->next) {
3350 1.1 christos if (header->serial <= search->serial &&
3351 1.1 christos !IGNORE(header) && EXISTS(header))
3352 1.1 christos break;
3353 1.1 christos }
3354 1.1 christos if (header != NULL)
3355 1.3 christos active = true;
3356 1.1 christos else
3357 1.3 christos active = false;
3358 1.1 christos
3359 1.1 christos if (node->wild)
3360 1.3 christos wild = true;
3361 1.1 christos else
3362 1.3 christos wild = false;
3363 1.1 christos
3364 1.1 christos NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
3365 1.1 christos isc_rwlocktype_read);
3366 1.1 christos
3367 1.1 christos if (wild) {
3368 1.1 christos /*
3369 1.1 christos * Construct the wildcard name for this level.
3370 1.1 christos */
3371 1.1 christos dns_name_init(&name, NULL);
3372 1.1 christos dns_rbt_namefromnode(node, &name);
3373 1.1 christos wname = dns_fixedname_initname(&fwname);
3374 1.1 christos result = dns_name_concatenate(dns_wildcardname, &name,
3375 1.1 christos wname, NULL);
3376 1.1 christos j = i;
3377 1.1 christos while (result == ISC_R_SUCCESS && j != 0) {
3378 1.1 christos j--;
3379 1.1 christos level_node = search->chain.levels[j];
3380 1.1 christos dns_name_init(&name, NULL);
3381 1.1 christos dns_rbt_namefromnode(level_node, &name);
3382 1.1 christos result = dns_name_concatenate(wname,
3383 1.1 christos &name,
3384 1.1 christos wname,
3385 1.1 christos NULL);
3386 1.1 christos }
3387 1.1 christos if (result != ISC_R_SUCCESS)
3388 1.1 christos break;
3389 1.1 christos
3390 1.1 christos wnode = NULL;
3391 1.1 christos dns_rbtnodechain_init(&wchain, NULL);
3392 1.1 christos result = dns_rbt_findnode(rbtdb->tree, wname,
3393 1.1 christos NULL, &wnode, &wchain,
3394 1.1 christos DNS_RBTFIND_EMPTYDATA,
3395 1.1 christos NULL, NULL);
3396 1.1 christos if (result == ISC_R_SUCCESS) {
3397 1.1 christos nodelock_t *lock;
3398 1.1 christos
3399 1.1 christos /*
3400 1.1 christos * We have found the wildcard node. If it
3401 1.1 christos * is active in the search's version, we're
3402 1.1 christos * done.
3403 1.1 christos */
3404 1.1 christos lock = &rbtdb->node_locks[wnode->locknum].lock;
3405 1.1 christos NODE_LOCK(lock, isc_rwlocktype_read);
3406 1.1 christos for (header = wnode->data;
3407 1.1 christos header != NULL;
3408 1.1 christos header = header->next) {
3409 1.1 christos if (header->serial <= search->serial &&
3410 1.1 christos !IGNORE(header) && EXISTS(header))
3411 1.1 christos break;
3412 1.1 christos }
3413 1.1 christos NODE_UNLOCK(lock, isc_rwlocktype_read);
3414 1.1 christos if (header != NULL ||
3415 1.1 christos activeempty(search, &wchain, wname)) {
3416 1.1 christos if (activeemtpynode(search, qname,
3417 1.1 christos wname)) {
3418 1.1 christos return (ISC_R_NOTFOUND);
3419 1.1 christos }
3420 1.1 christos /*
3421 1.1 christos * The wildcard node is active!
3422 1.1 christos *
3423 1.1 christos * Note: result is still ISC_R_SUCCESS
3424 1.1 christos * so we don't have to set it.
3425 1.1 christos */
3426 1.1 christos *nodep = wnode;
3427 1.1 christos break;
3428 1.1 christos }
3429 1.1 christos } else if (result != ISC_R_NOTFOUND &&
3430 1.1 christos result != DNS_R_PARTIALMATCH) {
3431 1.1 christos /*
3432 1.1 christos * An error has occurred. Bail out.
3433 1.1 christos */
3434 1.1 christos break;
3435 1.1 christos }
3436 1.1 christos }
3437 1.1 christos
3438 1.1 christos if (active) {
3439 1.1 christos /*
3440 1.1 christos * The level node is active. Any wildcarding
3441 1.1 christos * present at higher levels has no
3442 1.1 christos * effect and we're done.
3443 1.1 christos */
3444 1.1 christos result = ISC_R_NOTFOUND;
3445 1.1 christos break;
3446 1.1 christos }
3447 1.1 christos
3448 1.1 christos if (i > 0) {
3449 1.1 christos i--;
3450 1.1 christos node = search->chain.levels[i];
3451 1.1 christos } else
3452 1.3 christos done = true;
3453 1.1 christos } while (!done);
3454 1.1 christos
3455 1.1 christos return (result);
3456 1.1 christos }
3457 1.1 christos
3458 1.3 christos static bool
3459 1.1 christos matchparams(rdatasetheader_t *header, rbtdb_search_t *search)
3460 1.1 christos {
3461 1.1 christos dns_rdata_t rdata = DNS_RDATA_INIT;
3462 1.1 christos dns_rdata_nsec3_t nsec3;
3463 1.1 christos unsigned char *raw; /* RDATASLAB */
3464 1.1 christos unsigned int rdlen, count;
3465 1.1 christos isc_region_t region;
3466 1.1 christos isc_result_t result;
3467 1.1 christos
3468 1.1 christos REQUIRE(header->type == dns_rdatatype_nsec3);
3469 1.1 christos
3470 1.1 christos raw = (unsigned char *)header + sizeof(*header);
3471 1.1 christos count = raw[0] * 256 + raw[1]; /* count */
3472 1.1 christos #if DNS_RDATASET_FIXED
3473 1.1 christos raw += count * 4 + 2;
3474 1.1 christos #else
3475 1.1 christos raw += 2;
3476 1.1 christos #endif
3477 1.1 christos while (count-- > 0) {
3478 1.1 christos rdlen = raw[0] * 256 + raw[1];
3479 1.1 christos #if DNS_RDATASET_FIXED
3480 1.1 christos raw += 4;
3481 1.1 christos #else
3482 1.1 christos raw += 2;
3483 1.1 christos #endif
3484 1.1 christos region.base = raw;
3485 1.1 christos region.length = rdlen;
3486 1.1 christos dns_rdata_fromregion(&rdata, search->rbtdb->common.rdclass,
3487 1.1 christos dns_rdatatype_nsec3, ®ion);
3488 1.1 christos raw += rdlen;
3489 1.1 christos result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
3490 1.1 christos INSIST(result == ISC_R_SUCCESS);
3491 1.1 christos if (nsec3.hash == search->rbtversion->hash &&
3492 1.1 christos nsec3.iterations == search->rbtversion->iterations &&
3493 1.1 christos nsec3.salt_length == search->rbtversion->salt_length &&
3494 1.1 christos memcmp(nsec3.salt, search->rbtversion->salt,
3495 1.1 christos nsec3.salt_length) == 0)
3496 1.3 christos return (true);
3497 1.1 christos dns_rdata_reset(&rdata);
3498 1.1 christos }
3499 1.3 christos return (false);
3500 1.1 christos }
3501 1.1 christos
3502 1.1 christos /*
3503 1.1 christos * Find node of the NSEC/NSEC3 record that is 'name'.
3504 1.1 christos */
3505 1.1 christos static inline isc_result_t
3506 1.1 christos previous_closest_nsec(dns_rdatatype_t type, rbtdb_search_t *search,
3507 1.1 christos dns_name_t *name, dns_name_t *origin,
3508 1.1 christos dns_rbtnode_t **nodep, dns_rbtnodechain_t *nsecchain,
3509 1.3 christos bool *firstp)
3510 1.1 christos {
3511 1.1 christos dns_fixedname_t ftarget;
3512 1.1 christos dns_name_t *target;
3513 1.1 christos dns_rbtnode_t *nsecnode;
3514 1.1 christos isc_result_t result;
3515 1.1 christos
3516 1.1 christos REQUIRE(nodep != NULL && *nodep == NULL);
3517 1.1 christos
3518 1.1 christos if (type == dns_rdatatype_nsec3) {
3519 1.1 christos result = dns_rbtnodechain_prev(&search->chain, NULL, NULL);
3520 1.1 christos if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
3521 1.1 christos return (result);
3522 1.1 christos result = dns_rbtnodechain_current(&search->chain, name, origin,
3523 1.1 christos nodep);
3524 1.1 christos return (result);
3525 1.1 christos }
3526 1.1 christos
3527 1.1 christos target = dns_fixedname_initname(&ftarget);
3528 1.1 christos
3529 1.1 christos for (;;) {
3530 1.1 christos if (*firstp) {
3531 1.1 christos /*
3532 1.1 christos * Construct the name of the second node to check.
3533 1.1 christos * It is the first node sought in the NSEC tree.
3534 1.1 christos */
3535 1.3 christos *firstp = false;
3536 1.1 christos dns_rbtnodechain_init(nsecchain, NULL);
3537 1.1 christos result = dns_name_concatenate(name, origin,
3538 1.1 christos target, NULL);
3539 1.1 christos if (result != ISC_R_SUCCESS)
3540 1.1 christos return (result);
3541 1.1 christos nsecnode = NULL;
3542 1.1 christos result = dns_rbt_findnode(search->rbtdb->nsec,
3543 1.1 christos target, NULL,
3544 1.1 christos &nsecnode, nsecchain,
3545 1.1 christos DNS_RBTFIND_NOOPTIONS,
3546 1.1 christos NULL, NULL);
3547 1.1 christos if (result == ISC_R_SUCCESS) {
3548 1.1 christos /*
3549 1.1 christos * Since this was the first loop, finding the
3550 1.1 christos * name in the NSEC tree implies that the first
3551 1.1 christos * node checked in the main tree had an
3552 1.1 christos * unacceptable NSEC record.
3553 1.1 christos * Try the previous node in the NSEC tree.
3554 1.1 christos */
3555 1.1 christos result = dns_rbtnodechain_prev(nsecchain,
3556 1.1 christos name, origin);
3557 1.1 christos if (result == DNS_R_NEWORIGIN)
3558 1.1 christos result = ISC_R_SUCCESS;
3559 1.1 christos } else if (result == ISC_R_NOTFOUND ||
3560 1.1 christos result == DNS_R_PARTIALMATCH) {
3561 1.1 christos result = dns_rbtnodechain_current(nsecchain,
3562 1.1 christos name, origin, NULL);
3563 1.1 christos if (result == ISC_R_NOTFOUND)
3564 1.1 christos result = ISC_R_NOMORE;
3565 1.1 christos }
3566 1.1 christos } else {
3567 1.1 christos /*
3568 1.1 christos * This is a second or later trip through the auxiliary
3569 1.1 christos * tree for the name of a third or earlier NSEC node in
3570 1.1 christos * the main tree. Previous trips through the NSEC tree
3571 1.1 christos * must have found nodes in the main tree with NSEC
3572 1.1 christos * records. Perhaps they lacked signature records.
3573 1.1 christos */
3574 1.1 christos result = dns_rbtnodechain_prev(nsecchain, name, origin);
3575 1.1 christos if (result == DNS_R_NEWORIGIN)
3576 1.1 christos result = ISC_R_SUCCESS;
3577 1.1 christos }
3578 1.1 christos if (result != ISC_R_SUCCESS)
3579 1.1 christos return (result);
3580 1.1 christos
3581 1.1 christos /*
3582 1.1 christos * Construct the name to seek in the main tree.
3583 1.1 christos */
3584 1.1 christos result = dns_name_concatenate(name, origin, target, NULL);
3585 1.1 christos if (result != ISC_R_SUCCESS)
3586 1.1 christos return (result);
3587 1.1 christos
3588 1.1 christos *nodep = NULL;
3589 1.1 christos result = dns_rbt_findnode(search->rbtdb->tree, target, NULL,
3590 1.1 christos nodep, &search->chain,
3591 1.1 christos DNS_RBTFIND_NOOPTIONS, NULL, NULL);
3592 1.1 christos if (result == ISC_R_SUCCESS)
3593 1.1 christos return (result);
3594 1.1 christos
3595 1.1 christos /*
3596 1.1 christos * There should always be a node in the main tree with the
3597 1.1 christos * same name as the node in the auxiliary NSEC tree, except for
3598 1.1 christos * nodes in the auxiliary tree that are awaiting deletion.
3599 1.1 christos */
3600 1.1 christos if (result != DNS_R_PARTIALMATCH && result != ISC_R_NOTFOUND) {
3601 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
3602 1.1 christos DNS_LOGMODULE_CACHE, ISC_LOG_ERROR,
3603 1.1 christos "previous_closest_nsec(): %s",
3604 1.1 christos isc_result_totext(result));
3605 1.1 christos return (DNS_R_BADDB);
3606 1.1 christos }
3607 1.1 christos }
3608 1.1 christos }
3609 1.1 christos
3610 1.1 christos /*
3611 1.1 christos * Find the NSEC/NSEC3 which is or before the current point on the
3612 1.1 christos * search chain. For NSEC3 records only NSEC3 records that match the
3613 1.1 christos * current NSEC3PARAM record are considered.
3614 1.1 christos */
3615 1.1 christos static inline isc_result_t
3616 1.1 christos find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
3617 1.1 christos dns_name_t *foundname, dns_rdataset_t *rdataset,
3618 1.1 christos dns_rdataset_t *sigrdataset, dns_rbt_t *tree,
3619 1.1 christos dns_db_secure_t secure)
3620 1.1 christos {
3621 1.1 christos dns_rbtnode_t *node, *prevnode;
3622 1.1 christos rdatasetheader_t *header, *header_next, *found, *foundsig;
3623 1.1 christos dns_rbtnodechain_t nsecchain;
3624 1.3 christos bool empty_node;
3625 1.1 christos isc_result_t result;
3626 1.1 christos dns_fixedname_t fname, forigin;
3627 1.1 christos dns_name_t *name, *origin;
3628 1.1 christos dns_rdatatype_t type;
3629 1.1 christos rbtdb_rdatatype_t sigtype;
3630 1.3 christos bool wraps;
3631 1.3 christos bool first = true;
3632 1.3 christos bool need_sig = (secure == dns_db_secure);
3633 1.1 christos
3634 1.1 christos if (tree == search->rbtdb->nsec3) {
3635 1.1 christos type = dns_rdatatype_nsec3;
3636 1.1 christos sigtype = RBTDB_RDATATYPE_SIGNSEC3;
3637 1.3 christos wraps = true;
3638 1.1 christos } else {
3639 1.1 christos type = dns_rdatatype_nsec;
3640 1.1 christos sigtype = RBTDB_RDATATYPE_SIGNSEC;
3641 1.3 christos wraps = false;
3642 1.1 christos }
3643 1.1 christos
3644 1.1 christos /*
3645 1.1 christos * Use the auxiliary tree only starting with the second node in the
3646 1.1 christos * hope that the original node will be right much of the time.
3647 1.1 christos */
3648 1.1 christos name = dns_fixedname_initname(&fname);
3649 1.1 christos origin = dns_fixedname_initname(&forigin);
3650 1.1 christos again:
3651 1.1 christos node = NULL;
3652 1.1 christos prevnode = NULL;
3653 1.1 christos result = dns_rbtnodechain_current(&search->chain, name, origin, &node);
3654 1.1 christos if (result != ISC_R_SUCCESS)
3655 1.1 christos return (result);
3656 1.1 christos do {
3657 1.1 christos NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock),
3658 1.1 christos isc_rwlocktype_read);
3659 1.1 christos found = NULL;
3660 1.1 christos foundsig = NULL;
3661 1.3 christos empty_node = true;
3662 1.1 christos for (header = node->data;
3663 1.1 christos header != NULL;
3664 1.1 christos header = header_next) {
3665 1.1 christos header_next = header->next;
3666 1.1 christos /*
3667 1.1 christos * Look for an active, extant NSEC or RRSIG NSEC.
3668 1.1 christos */
3669 1.1 christos do {
3670 1.1 christos if (header->serial <= search->serial &&
3671 1.1 christos !IGNORE(header)) {
3672 1.1 christos /*
3673 1.1 christos * Is this a "this rdataset doesn't
3674 1.1 christos * exist" record?
3675 1.1 christos */
3676 1.1 christos if (NONEXISTENT(header))
3677 1.1 christos header = NULL;
3678 1.1 christos break;
3679 1.1 christos } else
3680 1.1 christos header = header->down;
3681 1.1 christos } while (header != NULL);
3682 1.1 christos if (header != NULL) {
3683 1.1 christos /*
3684 1.1 christos * We now know that there is at least one
3685 1.1 christos * active rdataset at this node.
3686 1.1 christos */
3687 1.3 christos empty_node = false;
3688 1.1 christos if (header->type == type) {
3689 1.1 christos found = header;
3690 1.1 christos if (foundsig != NULL)
3691 1.1 christos break;
3692 1.1 christos } else if (header->type == sigtype) {
3693 1.1 christos foundsig = header;
3694 1.1 christos if (found != NULL)
3695 1.1 christos break;
3696 1.1 christos }
3697 1.1 christos }
3698 1.1 christos }
3699 1.1 christos if (!empty_node) {
3700 1.1 christos if (found != NULL && search->rbtversion->havensec3 &&
3701 1.1 christos found->type == dns_rdatatype_nsec3 &&
3702 1.1 christos !matchparams(found, search)) {
3703 1.3 christos empty_node = true;
3704 1.1 christos found = NULL;
3705 1.1 christos foundsig = NULL;
3706 1.1 christos result = previous_closest_nsec(type, search,
3707 1.1 christos name, origin,
3708 1.1 christos &prevnode, NULL,
3709 1.1 christos NULL);
3710 1.1 christos } else if (found != NULL &&
3711 1.1 christos (foundsig != NULL || !need_sig)) {
3712 1.1 christos /*
3713 1.1 christos * We've found the right NSEC/NSEC3 record.
3714 1.1 christos *
3715 1.1 christos * Note: for this to really be the right
3716 1.1 christos * NSEC record, it's essential that the NSEC
3717 1.1 christos * records of any nodes obscured by a zone
3718 1.1 christos * cut have been removed; we assume this is
3719 1.1 christos * the case.
3720 1.1 christos */
3721 1.1 christos result = dns_name_concatenate(name, origin,
3722 1.1 christos foundname, NULL);
3723 1.1 christos if (result == ISC_R_SUCCESS) {
3724 1.1 christos if (nodep != NULL) {
3725 1.1 christos new_reference(search->rbtdb,
3726 1.1 christos node);
3727 1.1 christos *nodep = node;
3728 1.1 christos }
3729 1.1 christos bind_rdataset(search->rbtdb, node,
3730 1.1 christos found, search->now,
3731 1.1 christos rdataset);
3732 1.1 christos if (foundsig != NULL)
3733 1.1 christos bind_rdataset(search->rbtdb,
3734 1.1 christos node,
3735 1.1 christos foundsig,
3736 1.1 christos search->now,
3737 1.1 christos sigrdataset);
3738 1.1 christos }
3739 1.1 christos } else if (found == NULL && foundsig == NULL) {
3740 1.1 christos /*
3741 1.1 christos * This node is active, but has no NSEC or
3742 1.1 christos * RRSIG NSEC. That means it's glue or
3743 1.1 christos * other obscured zone data that isn't
3744 1.1 christos * relevant for our search. Treat the
3745 1.1 christos * node as if it were empty and keep looking.
3746 1.1 christos */
3747 1.3 christos empty_node = true;
3748 1.1 christos result = previous_closest_nsec(type, search,
3749 1.1 christos name, origin,
3750 1.1 christos &prevnode,
3751 1.1 christos &nsecchain,
3752 1.1 christos &first);
3753 1.1 christos } else {
3754 1.1 christos /*
3755 1.1 christos * We found an active node, but either the
3756 1.1 christos * NSEC or the RRSIG NSEC is missing. This
3757 1.1 christos * shouldn't happen.
3758 1.1 christos */
3759 1.1 christos result = DNS_R_BADDB;
3760 1.1 christos }
3761 1.1 christos } else {
3762 1.1 christos /*
3763 1.1 christos * This node isn't active. We've got to keep
3764 1.1 christos * looking.
3765 1.1 christos */
3766 1.1 christos result = previous_closest_nsec(type, search,
3767 1.1 christos name, origin, &prevnode,
3768 1.1 christos &nsecchain, &first);
3769 1.1 christos }
3770 1.1 christos NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
3771 1.1 christos isc_rwlocktype_read);
3772 1.1 christos node = prevnode;
3773 1.1 christos prevnode = NULL;
3774 1.1 christos } while (empty_node && result == ISC_R_SUCCESS);
3775 1.1 christos
3776 1.1 christos if (!first)
3777 1.1 christos dns_rbtnodechain_invalidate(&nsecchain);
3778 1.1 christos
3779 1.1 christos if (result == ISC_R_NOMORE && wraps) {
3780 1.1 christos result = dns_rbtnodechain_last(&search->chain, tree,
3781 1.1 christos NULL, NULL);
3782 1.1 christos if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
3783 1.3 christos wraps = false;
3784 1.1 christos goto again;
3785 1.1 christos }
3786 1.1 christos }
3787 1.1 christos
3788 1.1 christos /*
3789 1.1 christos * If the result is ISC_R_NOMORE, then we got to the beginning of
3790 1.1 christos * the database and didn't find a NSEC record. This shouldn't
3791 1.1 christos * happen.
3792 1.1 christos */
3793 1.1 christos if (result == ISC_R_NOMORE)
3794 1.1 christos result = DNS_R_BADDB;
3795 1.1 christos
3796 1.1 christos return (result);
3797 1.1 christos }
3798 1.1 christos
3799 1.1 christos static isc_result_t
3800 1.1 christos zone_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
3801 1.1 christos dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
3802 1.1 christos dns_dbnode_t **nodep, dns_name_t *foundname,
3803 1.1 christos dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
3804 1.1 christos {
3805 1.1 christos dns_rbtnode_t *node = NULL;
3806 1.1 christos isc_result_t result;
3807 1.1 christos rbtdb_search_t search;
3808 1.3 christos bool cname_ok = true;
3809 1.3 christos bool close_version = false;
3810 1.3 christos bool maybe_zonecut = false;
3811 1.3 christos bool at_zonecut = false;
3812 1.3 christos bool wild;
3813 1.3 christos bool empty_node;
3814 1.1 christos rdatasetheader_t *header, *header_next, *found, *nsecheader;
3815 1.1 christos rdatasetheader_t *foundsig, *cnamesig, *nsecsig;
3816 1.1 christos rbtdb_rdatatype_t sigtype;
3817 1.3 christos bool active;
3818 1.1 christos dns_rbtnodechain_t chain;
3819 1.1 christos nodelock_t *lock;
3820 1.1 christos dns_rbt_t *tree;
3821 1.1 christos
3822 1.1 christos search.rbtdb = (dns_rbtdb_t *)db;
3823 1.1 christos
3824 1.1 christos REQUIRE(VALID_RBTDB(search.rbtdb));
3825 1.1 christos INSIST(version == NULL ||
3826 1.1 christos ((rbtdb_version_t *)version)->rbtdb == (dns_rbtdb_t *)db);
3827 1.1 christos
3828 1.1 christos /*
3829 1.1 christos * We don't care about 'now'.
3830 1.1 christos */
3831 1.1 christos UNUSED(now);
3832 1.1 christos
3833 1.1 christos /*
3834 1.1 christos * If the caller didn't supply a version, attach to the current
3835 1.1 christos * version.
3836 1.1 christos */
3837 1.1 christos if (version == NULL) {
3838 1.1 christos currentversion(db, &version);
3839 1.3 christos close_version = true;
3840 1.1 christos }
3841 1.1 christos
3842 1.1 christos search.rbtversion = version;
3843 1.1 christos search.serial = search.rbtversion->serial;
3844 1.1 christos search.options = options;
3845 1.3 christos search.copy_name = false;
3846 1.3 christos search.need_cleanup = false;
3847 1.3 christos search.wild = false;
3848 1.1 christos search.zonecut = NULL;
3849 1.1 christos dns_fixedname_init(&search.zonecut_name);
3850 1.1 christos dns_rbtnodechain_init(&search.chain, search.rbtdb->common.mctx);
3851 1.1 christos search.now = 0;
3852 1.1 christos
3853 1.1 christos /*
3854 1.1 christos * 'wild' will be true iff. we've matched a wildcard.
3855 1.1 christos */
3856 1.3 christos wild = false;
3857 1.1 christos
3858 1.1 christos RWLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read);
3859 1.1 christos
3860 1.1 christos /*
3861 1.1 christos * Search down from the root of the tree. If, while going down, we
3862 1.1 christos * encounter a callback node, zone_zonecut_callback() will search the
3863 1.1 christos * rdatasets at the zone cut for active DNAME or NS rdatasets.
3864 1.1 christos */
3865 1.1 christos tree = (options & DNS_DBFIND_FORCENSEC3) != 0 ? search.rbtdb->nsec3 :
3866 1.1 christos search.rbtdb->tree;
3867 1.1 christos result = dns_rbt_findnode(tree, name, foundname, &node,
3868 1.1 christos &search.chain, DNS_RBTFIND_EMPTYDATA,
3869 1.1 christos zone_zonecut_callback, &search);
3870 1.1 christos
3871 1.1 christos if (result == DNS_R_PARTIALMATCH) {
3872 1.1 christos partial_match:
3873 1.1 christos if (search.zonecut != NULL) {
3874 1.1 christos result = setup_delegation(&search, nodep, foundname,
3875 1.1 christos rdataset, sigrdataset);
3876 1.1 christos goto tree_exit;
3877 1.1 christos }
3878 1.1 christos
3879 1.1 christos if (search.wild) {
3880 1.1 christos /*
3881 1.1 christos * At least one of the levels in the search chain
3882 1.1 christos * potentially has a wildcard. For each such level,
3883 1.1 christos * we must see if there's a matching wildcard active
3884 1.1 christos * in the current version.
3885 1.1 christos */
3886 1.1 christos result = find_wildcard(&search, &node, name);
3887 1.1 christos if (result == ISC_R_SUCCESS) {
3888 1.1 christos result = dns_name_copy(name, foundname, NULL);
3889 1.1 christos if (result != ISC_R_SUCCESS)
3890 1.1 christos goto tree_exit;
3891 1.3 christos wild = true;
3892 1.1 christos goto found;
3893 1.1 christos }
3894 1.1 christos else if (result != ISC_R_NOTFOUND)
3895 1.1 christos goto tree_exit;
3896 1.1 christos }
3897 1.1 christos
3898 1.1 christos chain = search.chain;
3899 1.1 christos active = activeempty(&search, &chain, name);
3900 1.1 christos
3901 1.1 christos /*
3902 1.1 christos * If we're here, then the name does not exist, is not
3903 1.1 christos * beneath a zonecut, and there's no matching wildcard.
3904 1.1 christos */
3905 1.1 christos if ((search.rbtversion->secure == dns_db_secure &&
3906 1.1 christos !search.rbtversion->havensec3) ||
3907 1.1 christos (search.options & DNS_DBFIND_FORCENSEC) != 0 ||
3908 1.1 christos (search.options & DNS_DBFIND_FORCENSEC3) != 0)
3909 1.1 christos {
3910 1.1 christos result = find_closest_nsec(&search, nodep, foundname,
3911 1.1 christos rdataset, sigrdataset, tree,
3912 1.1 christos search.rbtversion->secure);
3913 1.1 christos if (result == ISC_R_SUCCESS)
3914 1.1 christos result = active ? DNS_R_EMPTYNAME :
3915 1.1 christos DNS_R_NXDOMAIN;
3916 1.1 christos } else
3917 1.1 christos result = active ? DNS_R_EMPTYNAME : DNS_R_NXDOMAIN;
3918 1.1 christos goto tree_exit;
3919 1.1 christos } else if (result != ISC_R_SUCCESS)
3920 1.1 christos goto tree_exit;
3921 1.1 christos
3922 1.1 christos found:
3923 1.1 christos /*
3924 1.1 christos * We have found a node whose name is the desired name, or we
3925 1.1 christos * have matched a wildcard.
3926 1.1 christos */
3927 1.1 christos
3928 1.1 christos if (search.zonecut != NULL) {
3929 1.1 christos /*
3930 1.1 christos * If we're beneath a zone cut, we don't want to look for
3931 1.1 christos * CNAMEs because they're not legitimate zone glue.
3932 1.1 christos */
3933 1.3 christos cname_ok = false;
3934 1.1 christos } else {
3935 1.1 christos /*
3936 1.1 christos * The node may be a zone cut itself. If it might be one,
3937 1.1 christos * make sure we check for it later.
3938 1.1 christos *
3939 1.1 christos * DS records live above the zone cut in ordinary zone so
3940 1.1 christos * we want to ignore any referral.
3941 1.1 christos *
3942 1.1 christos * Stub zones don't have anything "above" the delgation so
3943 1.1 christos * we always return a referral.
3944 1.1 christos */
3945 1.1 christos if (node->find_callback &&
3946 1.1 christos ((node != search.rbtdb->origin_node &&
3947 1.1 christos !dns_rdatatype_atparent(type)) ||
3948 1.1 christos IS_STUB(search.rbtdb)))
3949 1.3 christos maybe_zonecut = true;
3950 1.1 christos }
3951 1.1 christos
3952 1.1 christos /*
3953 1.1 christos * Certain DNSSEC types are not subject to CNAME matching
3954 1.1 christos * (RFC4035, section 2.5 and RFC3007).
3955 1.1 christos *
3956 1.1 christos * We don't check for RRSIG, because we don't store RRSIG records
3957 1.1 christos * directly.
3958 1.1 christos */
3959 1.1 christos if (type == dns_rdatatype_key || type == dns_rdatatype_nsec)
3960 1.3 christos cname_ok = false;
3961 1.1 christos
3962 1.1 christos /*
3963 1.1 christos * We now go looking for rdata...
3964 1.1 christos */
3965 1.1 christos
3966 1.1 christos lock = &search.rbtdb->node_locks[node->locknum].lock;
3967 1.1 christos NODE_LOCK(lock, isc_rwlocktype_read);
3968 1.1 christos
3969 1.1 christos found = NULL;
3970 1.1 christos foundsig = NULL;
3971 1.1 christos sigtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type);
3972 1.1 christos nsecheader = NULL;
3973 1.1 christos nsecsig = NULL;
3974 1.1 christos cnamesig = NULL;
3975 1.3 christos empty_node = true;
3976 1.1 christos for (header = node->data; header != NULL; header = header_next) {
3977 1.1 christos header_next = header->next;
3978 1.1 christos /*
3979 1.1 christos * Look for an active, extant rdataset.
3980 1.1 christos */
3981 1.1 christos do {
3982 1.1 christos if (header->serial <= search.serial &&
3983 1.1 christos !IGNORE(header)) {
3984 1.1 christos /*
3985 1.1 christos * Is this a "this rdataset doesn't
3986 1.1 christos * exist" record?
3987 1.1 christos */
3988 1.1 christos if (NONEXISTENT(header))
3989 1.1 christos header = NULL;
3990 1.1 christos break;
3991 1.1 christos } else
3992 1.1 christos header = header->down;
3993 1.1 christos } while (header != NULL);
3994 1.1 christos if (header != NULL) {
3995 1.1 christos /*
3996 1.1 christos * We now know that there is at least one active
3997 1.1 christos * rdataset at this node.
3998 1.1 christos */
3999 1.3 christos empty_node = false;
4000 1.1 christos
4001 1.1 christos /*
4002 1.1 christos * Do special zone cut handling, if requested.
4003 1.1 christos */
4004 1.1 christos if (maybe_zonecut &&
4005 1.1 christos header->type == dns_rdatatype_ns) {
4006 1.1 christos /*
4007 1.1 christos * We increment the reference count on node to
4008 1.1 christos * ensure that search->zonecut_rdataset will
4009 1.1 christos * still be valid later.
4010 1.1 christos */
4011 1.1 christos new_reference(search.rbtdb, node);
4012 1.1 christos search.zonecut = node;
4013 1.1 christos search.zonecut_rdataset = header;
4014 1.1 christos search.zonecut_sigrdataset = NULL;
4015 1.3 christos search.need_cleanup = true;
4016 1.3 christos maybe_zonecut = false;
4017 1.3 christos at_zonecut = true;
4018 1.1 christos /*
4019 1.1 christos * It is not clear if KEY should still be
4020 1.1 christos * allowed at the parent side of the zone
4021 1.1 christos * cut or not. It is needed for RFC3007
4022 1.1 christos * validated updates.
4023 1.1 christos */
4024 1.1 christos if ((search.options & DNS_DBFIND_GLUEOK) == 0
4025 1.1 christos && type != dns_rdatatype_nsec
4026 1.1 christos && type != dns_rdatatype_key) {
4027 1.1 christos /*
4028 1.1 christos * Glue is not OK, but any answer we
4029 1.1 christos * could return would be glue. Return
4030 1.1 christos * the delegation.
4031 1.1 christos */
4032 1.1 christos found = NULL;
4033 1.1 christos break;
4034 1.1 christos }
4035 1.1 christos if (found != NULL && foundsig != NULL)
4036 1.1 christos break;
4037 1.1 christos }
4038 1.1 christos
4039 1.1 christos
4040 1.1 christos /*
4041 1.1 christos * If the NSEC3 record doesn't match the chain
4042 1.1 christos * we are using behave as if it isn't here.
4043 1.1 christos */
4044 1.1 christos if (header->type == dns_rdatatype_nsec3 &&
4045 1.1 christos !matchparams(header, &search)) {
4046 1.1 christos NODE_UNLOCK(lock, isc_rwlocktype_read);
4047 1.1 christos goto partial_match;
4048 1.1 christos }
4049 1.1 christos /*
4050 1.1 christos * If we found a type we were looking for,
4051 1.1 christos * remember it.
4052 1.1 christos */
4053 1.1 christos if (header->type == type ||
4054 1.1 christos type == dns_rdatatype_any ||
4055 1.1 christos (header->type == dns_rdatatype_cname &&
4056 1.1 christos cname_ok)) {
4057 1.1 christos /*
4058 1.1 christos * We've found the answer!
4059 1.1 christos */
4060 1.1 christos found = header;
4061 1.1 christos if (header->type == dns_rdatatype_cname &&
4062 1.1 christos cname_ok) {
4063 1.1 christos /*
4064 1.1 christos * We may be finding a CNAME instead
4065 1.1 christos * of the desired type.
4066 1.1 christos *
4067 1.1 christos * If we've already got the CNAME RRSIG,
4068 1.1 christos * use it, otherwise change sigtype
4069 1.1 christos * so that we find it.
4070 1.1 christos */
4071 1.1 christos if (cnamesig != NULL)
4072 1.1 christos foundsig = cnamesig;
4073 1.1 christos else
4074 1.1 christos sigtype =
4075 1.1 christos RBTDB_RDATATYPE_SIGCNAME;
4076 1.1 christos }
4077 1.1 christos /*
4078 1.1 christos * If we've got all we need, end the search.
4079 1.1 christos */
4080 1.1 christos if (!maybe_zonecut && foundsig != NULL)
4081 1.1 christos break;
4082 1.1 christos } else if (header->type == sigtype) {
4083 1.1 christos /*
4084 1.1 christos * We've found the RRSIG rdataset for our
4085 1.1 christos * target type. Remember it.
4086 1.1 christos */
4087 1.1 christos foundsig = header;
4088 1.1 christos /*
4089 1.1 christos * If we've got all we need, end the search.
4090 1.1 christos */
4091 1.1 christos if (!maybe_zonecut && found != NULL)
4092 1.1 christos break;
4093 1.1 christos } else if (header->type == dns_rdatatype_nsec &&
4094 1.1 christos !search.rbtversion->havensec3) {
4095 1.1 christos /*
4096 1.1 christos * Remember a NSEC rdataset even if we're
4097 1.1 christos * not specifically looking for it, because
4098 1.1 christos * we might need it later.
4099 1.1 christos */
4100 1.1 christos nsecheader = header;
4101 1.1 christos } else if (header->type == RBTDB_RDATATYPE_SIGNSEC &&
4102 1.1 christos !search.rbtversion->havensec3) {
4103 1.1 christos /*
4104 1.1 christos * If we need the NSEC rdataset, we'll also
4105 1.1 christos * need its signature.
4106 1.1 christos */
4107 1.1 christos nsecsig = header;
4108 1.1 christos } else if (cname_ok &&
4109 1.1 christos header->type == RBTDB_RDATATYPE_SIGCNAME) {
4110 1.1 christos /*
4111 1.1 christos * If we get a CNAME match, we'll also need
4112 1.1 christos * its signature.
4113 1.1 christos */
4114 1.1 christos cnamesig = header;
4115 1.1 christos }
4116 1.1 christos }
4117 1.1 christos }
4118 1.1 christos
4119 1.1 christos if (empty_node) {
4120 1.1 christos /*
4121 1.1 christos * We have an exact match for the name, but there are no
4122 1.1 christos * active rdatasets in the desired version. That means that
4123 1.1 christos * this node doesn't exist in the desired version, and that
4124 1.1 christos * we really have a partial match.
4125 1.1 christos */
4126 1.1 christos if (!wild) {
4127 1.1 christos NODE_UNLOCK(lock, isc_rwlocktype_read);
4128 1.1 christos goto partial_match;
4129 1.1 christos }
4130 1.1 christos }
4131 1.1 christos
4132 1.1 christos /*
4133 1.1 christos * If we didn't find what we were looking for...
4134 1.1 christos */
4135 1.1 christos if (found == NULL) {
4136 1.1 christos if (search.zonecut != NULL) {
4137 1.1 christos /*
4138 1.1 christos * We were trying to find glue at a node beneath a
4139 1.1 christos * zone cut, but didn't.
4140 1.1 christos *
4141 1.1 christos * Return the delegation.
4142 1.1 christos */
4143 1.1 christos NODE_UNLOCK(lock, isc_rwlocktype_read);
4144 1.1 christos result = setup_delegation(&search, nodep, foundname,
4145 1.1 christos rdataset, sigrdataset);
4146 1.1 christos goto tree_exit;
4147 1.1 christos }
4148 1.1 christos /*
4149 1.1 christos * The desired type doesn't exist.
4150 1.1 christos */
4151 1.1 christos result = DNS_R_NXRRSET;
4152 1.1 christos if (search.rbtversion->secure == dns_db_secure &&
4153 1.1 christos !search.rbtversion->havensec3 &&
4154 1.1 christos (nsecheader == NULL || nsecsig == NULL)) {
4155 1.1 christos /*
4156 1.1 christos * The zone is secure but there's no NSEC,
4157 1.1 christos * or the NSEC has no signature!
4158 1.1 christos */
4159 1.1 christos if (!wild) {
4160 1.1 christos result = DNS_R_BADDB;
4161 1.1 christos goto node_exit;
4162 1.1 christos }
4163 1.1 christos
4164 1.1 christos NODE_UNLOCK(lock, isc_rwlocktype_read);
4165 1.1 christos result = find_closest_nsec(&search, nodep, foundname,
4166 1.1 christos rdataset, sigrdataset,
4167 1.1 christos search.rbtdb->tree,
4168 1.1 christos search.rbtversion->secure);
4169 1.1 christos if (result == ISC_R_SUCCESS)
4170 1.1 christos result = DNS_R_EMPTYWILD;
4171 1.1 christos goto tree_exit;
4172 1.1 christos }
4173 1.1 christos if ((search.options & DNS_DBFIND_FORCENSEC) != 0 &&
4174 1.1 christos nsecheader == NULL)
4175 1.1 christos {
4176 1.1 christos /*
4177 1.1 christos * There's no NSEC record, and we were told
4178 1.1 christos * to find one.
4179 1.1 christos */
4180 1.1 christos result = DNS_R_BADDB;
4181 1.1 christos goto node_exit;
4182 1.1 christos }
4183 1.1 christos if (nodep != NULL) {
4184 1.1 christos new_reference(search.rbtdb, node);
4185 1.1 christos *nodep = node;
4186 1.1 christos }
4187 1.1 christos if ((search.rbtversion->secure == dns_db_secure &&
4188 1.1 christos !search.rbtversion->havensec3) ||
4189 1.1 christos (search.options & DNS_DBFIND_FORCENSEC) != 0)
4190 1.1 christos {
4191 1.1 christos bind_rdataset(search.rbtdb, node, nsecheader,
4192 1.1 christos 0, rdataset);
4193 1.1 christos if (nsecsig != NULL)
4194 1.1 christos bind_rdataset(search.rbtdb, node,
4195 1.1 christos nsecsig, 0, sigrdataset);
4196 1.1 christos }
4197 1.1 christos if (wild)
4198 1.1 christos foundname->attributes |= DNS_NAMEATTR_WILDCARD;
4199 1.1 christos goto node_exit;
4200 1.1 christos }
4201 1.1 christos
4202 1.1 christos /*
4203 1.1 christos * We found what we were looking for, or we found a CNAME.
4204 1.1 christos */
4205 1.1 christos
4206 1.1 christos if (type != found->type &&
4207 1.1 christos type != dns_rdatatype_any &&
4208 1.1 christos found->type == dns_rdatatype_cname) {
4209 1.1 christos /*
4210 1.1 christos * We weren't doing an ANY query and we found a CNAME instead
4211 1.1 christos * of the type we were looking for, so we need to indicate
4212 1.1 christos * that result to the caller.
4213 1.1 christos */
4214 1.1 christos result = DNS_R_CNAME;
4215 1.1 christos } else if (search.zonecut != NULL) {
4216 1.1 christos /*
4217 1.1 christos * If we're beneath a zone cut, we must indicate that the
4218 1.1 christos * result is glue, unless we're actually at the zone cut
4219 1.1 christos * and the type is NSEC or KEY.
4220 1.1 christos */
4221 1.1 christos if (search.zonecut == node) {
4222 1.1 christos /*
4223 1.1 christos * It is not clear if KEY should still be
4224 1.1 christos * allowed at the parent side of the zone
4225 1.1 christos * cut or not. It is needed for RFC3007
4226 1.1 christos * validated updates.
4227 1.1 christos */
4228 1.1 christos if (type == dns_rdatatype_nsec ||
4229 1.1 christos type == dns_rdatatype_nsec3 ||
4230 1.1 christos type == dns_rdatatype_key)
4231 1.1 christos result = ISC_R_SUCCESS;
4232 1.1 christos else if (type == dns_rdatatype_any)
4233 1.1 christos result = DNS_R_ZONECUT;
4234 1.1 christos else
4235 1.1 christos result = DNS_R_GLUE;
4236 1.1 christos } else
4237 1.1 christos result = DNS_R_GLUE;
4238 1.1 christos /*
4239 1.1 christos * We might have found data that isn't glue, but was occluded
4240 1.1 christos * by a dynamic update. If the caller cares about this, they
4241 1.1 christos * will have told us to validate glue.
4242 1.1 christos *
4243 1.1 christos * XXX We should cache the glue validity state!
4244 1.1 christos */
4245 1.1 christos if (result == DNS_R_GLUE &&
4246 1.1 christos (search.options & DNS_DBFIND_VALIDATEGLUE) != 0 &&
4247 1.1 christos !valid_glue(&search, foundname, type, node)) {
4248 1.1 christos NODE_UNLOCK(lock, isc_rwlocktype_read);
4249 1.1 christos result = setup_delegation(&search, nodep, foundname,
4250 1.1 christos rdataset, sigrdataset);
4251 1.1 christos goto tree_exit;
4252 1.1 christos }
4253 1.1 christos } else {
4254 1.1 christos /*
4255 1.1 christos * An ordinary successful query!
4256 1.1 christos */
4257 1.1 christos result = ISC_R_SUCCESS;
4258 1.1 christos }
4259 1.1 christos
4260 1.1 christos if (nodep != NULL) {
4261 1.1 christos if (!at_zonecut)
4262 1.1 christos new_reference(search.rbtdb, node);
4263 1.1 christos else
4264 1.3 christos search.need_cleanup = false;
4265 1.1 christos *nodep = node;
4266 1.1 christos }
4267 1.1 christos
4268 1.1 christos if (type != dns_rdatatype_any) {
4269 1.1 christos bind_rdataset(search.rbtdb, node, found, 0, rdataset);
4270 1.1 christos if (foundsig != NULL)
4271 1.1 christos bind_rdataset(search.rbtdb, node, foundsig, 0,
4272 1.1 christos sigrdataset);
4273 1.1 christos }
4274 1.1 christos
4275 1.1 christos if (wild)
4276 1.1 christos foundname->attributes |= DNS_NAMEATTR_WILDCARD;
4277 1.1 christos
4278 1.1 christos node_exit:
4279 1.1 christos NODE_UNLOCK(lock, isc_rwlocktype_read);
4280 1.1 christos
4281 1.1 christos tree_exit:
4282 1.1 christos RWUNLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read);
4283 1.1 christos
4284 1.1 christos /*
4285 1.1 christos * If we found a zonecut but aren't going to use it, we have to
4286 1.1 christos * let go of it.
4287 1.1 christos */
4288 1.1 christos if (search.need_cleanup) {
4289 1.1 christos node = search.zonecut;
4290 1.1 christos INSIST(node != NULL);
4291 1.1 christos lock = &(search.rbtdb->node_locks[node->locknum].lock);
4292 1.1 christos
4293 1.1 christos NODE_LOCK(lock, isc_rwlocktype_read);
4294 1.1 christos decrement_reference(search.rbtdb, node, 0,
4295 1.1 christos isc_rwlocktype_read, isc_rwlocktype_none,
4296 1.3 christos false);
4297 1.1 christos NODE_UNLOCK(lock, isc_rwlocktype_read);
4298 1.1 christos }
4299 1.1 christos
4300 1.1 christos if (close_version)
4301 1.3 christos closeversion(db, &version, false);
4302 1.1 christos
4303 1.1 christos dns_rbtnodechain_reset(&search.chain);
4304 1.1 christos
4305 1.1 christos return (result);
4306 1.1 christos }
4307 1.1 christos
4308 1.1 christos static isc_result_t
4309 1.1 christos zone_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options,
4310 1.1 christos isc_stdtime_t now, dns_dbnode_t **nodep,
4311 1.3 christos dns_name_t *foundname, dns_name_t *dcname,
4312 1.1 christos dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
4313 1.1 christos {
4314 1.1 christos UNUSED(db);
4315 1.1 christos UNUSED(name);
4316 1.1 christos UNUSED(options);
4317 1.1 christos UNUSED(now);
4318 1.1 christos UNUSED(nodep);
4319 1.1 christos UNUSED(foundname);
4320 1.3 christos UNUSED(dcname);
4321 1.1 christos UNUSED(rdataset);
4322 1.1 christos UNUSED(sigrdataset);
4323 1.1 christos
4324 1.1 christos FATAL_ERROR(__FILE__, __LINE__, "zone_findzonecut() called!");
4325 1.1 christos
4326 1.1 christos /* NOTREACHED */
4327 1.1 christos return (ISC_R_NOTIMPLEMENTED);
4328 1.1 christos }
4329 1.1 christos
4330 1.3 christos static bool
4331 1.1 christos check_stale_header(dns_rbtnode_t *node, rdatasetheader_t *header,
4332 1.1 christos isc_rwlocktype_t *locktype, nodelock_t *lock,
4333 1.1 christos rbtdb_search_t *search, rdatasetheader_t **header_prev)
4334 1.1 christos {
4335 1.1 christos
4336 1.1 christos if (!ACTIVE(header, search->now)) {
4337 1.1 christos dns_ttl_t stale = header->rdh_ttl +
4338 1.1 christos search->rbtdb->serve_stale_ttl;
4339 1.1 christos /*
4340 1.1 christos * If this data is in the stale window keep it and if
4341 1.1 christos * DNS_DBFIND_STALEOK is not set we tell the caller to
4342 1.1 christos * skip this record.
4343 1.1 christos */
4344 1.1 christos if (KEEPSTALE(search->rbtdb) && stale > search->now) {
4345 1.1 christos header->attributes |= RDATASET_ATTR_STALE;
4346 1.1 christos *header_prev = header;
4347 1.1 christos return ((search->options & DNS_DBFIND_STALEOK) == 0);
4348 1.1 christos }
4349 1.1 christos
4350 1.1 christos /*
4351 1.1 christos * This rdataset is stale. If no one else is using the
4352 1.1 christos * node, we can clean it up right now, otherwise we mark
4353 1.1 christos * it as stale, and the node as dirty, so it will get
4354 1.1 christos * cleaned up later.
4355 1.1 christos */
4356 1.1 christos if ((header->rdh_ttl < search->now - RBTDB_VIRTUAL) &&
4357 1.1 christos (*locktype == isc_rwlocktype_write ||
4358 1.1 christos NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS))
4359 1.1 christos {
4360 1.1 christos /*
4361 1.1 christos * We update the node's status only when we can
4362 1.1 christos * get write access; otherwise, we leave others
4363 1.1 christos * to this work. Periodical cleaning will
4364 1.1 christos * eventually take the job as the last resort.
4365 1.1 christos * We won't downgrade the lock, since other
4366 1.1 christos * rdatasets are probably stale, too.
4367 1.1 christos */
4368 1.1 christos *locktype = isc_rwlocktype_write;
4369 1.1 christos
4370 1.3 christos if (isc_refcount_current(&node->references) == 0) {
4371 1.1 christos isc_mem_t *mctx;
4372 1.1 christos
4373 1.1 christos /*
4374 1.1 christos * header->down can be non-NULL if the
4375 1.1 christos * refcount has just decremented to 0
4376 1.1 christos * but decrement_reference() has not
4377 1.1 christos * performed clean_cache_node(), in
4378 1.1 christos * which case we need to purge the stale
4379 1.1 christos * headers first.
4380 1.1 christos */
4381 1.1 christos mctx = search->rbtdb->common.mctx;
4382 1.1 christos clean_stale_headers(search->rbtdb, mctx, header);
4383 1.1 christos if (*header_prev != NULL)
4384 1.1 christos (*header_prev)->next = header->next;
4385 1.1 christos else
4386 1.1 christos node->data = header->next;
4387 1.1 christos free_rdataset(search->rbtdb, mctx, header);
4388 1.1 christos } else {
4389 1.1 christos mark_header_ancient(search->rbtdb, header);
4390 1.1 christos *header_prev = header;
4391 1.1 christos }
4392 1.1 christos } else
4393 1.1 christos *header_prev = header;
4394 1.3 christos return (true);
4395 1.1 christos }
4396 1.3 christos return (false);
4397 1.1 christos }
4398 1.1 christos
4399 1.1 christos static isc_result_t
4400 1.1 christos cache_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
4401 1.1 christos rbtdb_search_t *search = arg;
4402 1.1 christos rdatasetheader_t *header, *header_prev, *header_next;
4403 1.1 christos rdatasetheader_t *dname_header, *sigdname_header;
4404 1.1 christos isc_result_t result;
4405 1.1 christos nodelock_t *lock;
4406 1.1 christos isc_rwlocktype_t locktype;
4407 1.1 christos
4408 1.1 christos /* XXX comment */
4409 1.1 christos
4410 1.1 christos REQUIRE(search->zonecut == NULL);
4411 1.1 christos
4412 1.1 christos /*
4413 1.1 christos * Keep compiler silent.
4414 1.1 christos */
4415 1.1 christos UNUSED(name);
4416 1.1 christos
4417 1.1 christos lock = &(search->rbtdb->node_locks[node->locknum].lock);
4418 1.1 christos locktype = isc_rwlocktype_read;
4419 1.1 christos NODE_LOCK(lock, locktype);
4420 1.1 christos
4421 1.1 christos /*
4422 1.1 christos * Look for a DNAME or RRSIG DNAME rdataset.
4423 1.1 christos */
4424 1.1 christos dname_header = NULL;
4425 1.1 christos sigdname_header = NULL;
4426 1.1 christos header_prev = NULL;
4427 1.1 christos for (header = node->data; header != NULL; header = header_next) {
4428 1.1 christos header_next = header->next;
4429 1.1 christos if (check_stale_header(node, header,
4430 1.1 christos &locktype, lock, search,
4431 1.1 christos &header_prev)) {
4432 1.1 christos /* Do nothing. */
4433 1.1 christos } else if (header->type == dns_rdatatype_dname &&
4434 1.1 christos EXISTS(header)) {
4435 1.1 christos dname_header = header;
4436 1.1 christos header_prev = header;
4437 1.1 christos } else if (header->type == RBTDB_RDATATYPE_SIGDNAME &&
4438 1.1 christos EXISTS(header)) {
4439 1.1 christos sigdname_header = header;
4440 1.1 christos header_prev = header;
4441 1.1 christos } else
4442 1.1 christos header_prev = header;
4443 1.1 christos }
4444 1.1 christos
4445 1.1 christos if (dname_header != NULL &&
4446 1.1 christos (!DNS_TRUST_PENDING(dname_header->trust) ||
4447 1.1 christos (search->options & DNS_DBFIND_PENDINGOK) != 0)) {
4448 1.1 christos /*
4449 1.1 christos * We increment the reference count on node to ensure that
4450 1.1 christos * search->zonecut_rdataset will still be valid later.
4451 1.1 christos */
4452 1.1 christos new_reference(search->rbtdb, node);
4453 1.1 christos INSIST(!ISC_LINK_LINKED(node, deadlink));
4454 1.1 christos search->zonecut = node;
4455 1.1 christos search->zonecut_rdataset = dname_header;
4456 1.1 christos search->zonecut_sigrdataset = sigdname_header;
4457 1.3 christos search->need_cleanup = true;
4458 1.1 christos result = DNS_R_PARTIALMATCH;
4459 1.1 christos } else
4460 1.1 christos result = DNS_R_CONTINUE;
4461 1.1 christos
4462 1.1 christos NODE_UNLOCK(lock, locktype);
4463 1.1 christos
4464 1.1 christos return (result);
4465 1.1 christos }
4466 1.1 christos
4467 1.1 christos static inline isc_result_t
4468 1.1 christos find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node,
4469 1.1 christos dns_dbnode_t **nodep, dns_name_t *foundname,
4470 1.1 christos dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
4471 1.1 christos {
4472 1.1 christos unsigned int i;
4473 1.1 christos dns_rbtnode_t *level_node;
4474 1.1 christos rdatasetheader_t *header, *header_prev, *header_next;
4475 1.1 christos rdatasetheader_t *found, *foundsig;
4476 1.1 christos isc_result_t result = ISC_R_NOTFOUND;
4477 1.1 christos dns_name_t name;
4478 1.1 christos dns_rbtdb_t *rbtdb;
4479 1.3 christos bool done;
4480 1.1 christos nodelock_t *lock;
4481 1.1 christos isc_rwlocktype_t locktype;
4482 1.1 christos
4483 1.1 christos /*
4484 1.1 christos * Caller must be holding the tree lock.
4485 1.1 christos */
4486 1.1 christos
4487 1.1 christos rbtdb = search->rbtdb;
4488 1.1 christos i = search->chain.level_matches;
4489 1.3 christos done = false;
4490 1.1 christos do {
4491 1.1 christos locktype = isc_rwlocktype_read;
4492 1.1 christos lock = &rbtdb->node_locks[node->locknum].lock;
4493 1.1 christos NODE_LOCK(lock, locktype);
4494 1.1 christos
4495 1.1 christos /*
4496 1.1 christos * Look for NS and RRSIG NS rdatasets.
4497 1.1 christos */
4498 1.1 christos found = NULL;
4499 1.1 christos foundsig = NULL;
4500 1.1 christos header_prev = NULL;
4501 1.1 christos for (header = node->data; header != NULL; header = header_next) {
4502 1.1 christos header_next = header->next;
4503 1.1 christos if (check_stale_header(node, header,
4504 1.1 christos &locktype, lock, search,
4505 1.1 christos &header_prev)) {
4506 1.1 christos /* Do nothing. */
4507 1.1 christos } else if (EXISTS(header)) {
4508 1.1 christos /*
4509 1.1 christos * We've found an extant rdataset. See if
4510 1.1 christos * we're interested in it.
4511 1.1 christos */
4512 1.1 christos if (header->type == dns_rdatatype_ns) {
4513 1.1 christos found = header;
4514 1.1 christos if (foundsig != NULL)
4515 1.1 christos break;
4516 1.1 christos } else if (header->type ==
4517 1.1 christos RBTDB_RDATATYPE_SIGNS) {
4518 1.1 christos foundsig = header;
4519 1.1 christos if (found != NULL)
4520 1.1 christos break;
4521 1.1 christos }
4522 1.1 christos header_prev = header;
4523 1.1 christos } else
4524 1.1 christos header_prev = header;
4525 1.1 christos }
4526 1.1 christos
4527 1.1 christos if (found != NULL) {
4528 1.1 christos /*
4529 1.1 christos * If we have to set foundname, we do it before
4530 1.1 christos * anything else. If we were to set foundname after
4531 1.1 christos * we had set nodep or bound the rdataset, then we'd
4532 1.1 christos * have to undo that work if dns_name_concatenate()
4533 1.1 christos * failed. By setting foundname first, there's
4534 1.1 christos * nothing to undo if we have trouble.
4535 1.1 christos */
4536 1.1 christos if (foundname != NULL) {
4537 1.1 christos dns_name_init(&name, NULL);
4538 1.1 christos dns_rbt_namefromnode(node, &name);
4539 1.1 christos result = dns_name_copy(&name, foundname, NULL);
4540 1.1 christos while (result == ISC_R_SUCCESS && i > 0) {
4541 1.1 christos i--;
4542 1.1 christos level_node = search->chain.levels[i];
4543 1.1 christos dns_name_init(&name, NULL);
4544 1.1 christos dns_rbt_namefromnode(level_node,
4545 1.1 christos &name);
4546 1.1 christos result =
4547 1.1 christos dns_name_concatenate(foundname,
4548 1.1 christos &name,
4549 1.1 christos foundname,
4550 1.1 christos NULL);
4551 1.1 christos }
4552 1.1 christos if (result != ISC_R_SUCCESS) {
4553 1.1 christos *nodep = NULL;
4554 1.1 christos goto node_exit;
4555 1.1 christos }
4556 1.1 christos }
4557 1.1 christos result = DNS_R_DELEGATION;
4558 1.1 christos if (nodep != NULL) {
4559 1.1 christos new_reference(search->rbtdb, node);
4560 1.1 christos *nodep = node;
4561 1.1 christos }
4562 1.1 christos bind_rdataset(search->rbtdb, node, found, search->now,
4563 1.1 christos rdataset);
4564 1.1 christos if (foundsig != NULL)
4565 1.1 christos bind_rdataset(search->rbtdb, node, foundsig,
4566 1.1 christos search->now, sigrdataset);
4567 1.1 christos if (need_headerupdate(found, search->now) ||
4568 1.1 christos (foundsig != NULL &&
4569 1.1 christos need_headerupdate(foundsig, search->now))) {
4570 1.1 christos if (locktype != isc_rwlocktype_write) {
4571 1.1 christos NODE_UNLOCK(lock, locktype);
4572 1.1 christos NODE_LOCK(lock, isc_rwlocktype_write);
4573 1.1 christos locktype = isc_rwlocktype_write;
4574 1.1 christos POST(locktype);
4575 1.1 christos }
4576 1.1 christos if (need_headerupdate(found, search->now))
4577 1.1 christos update_header(search->rbtdb, found,
4578 1.1 christos search->now);
4579 1.1 christos if (foundsig != NULL &&
4580 1.1 christos need_headerupdate(foundsig, search->now)) {
4581 1.1 christos update_header(search->rbtdb, foundsig,
4582 1.1 christos search->now);
4583 1.1 christos }
4584 1.1 christos }
4585 1.1 christos }
4586 1.1 christos
4587 1.1 christos node_exit:
4588 1.1 christos NODE_UNLOCK(lock, locktype);
4589 1.1 christos
4590 1.1 christos if (found == NULL && i > 0) {
4591 1.1 christos i--;
4592 1.1 christos node = search->chain.levels[i];
4593 1.1 christos } else
4594 1.3 christos done = true;
4595 1.1 christos
4596 1.1 christos } while (!done);
4597 1.1 christos
4598 1.1 christos return (result);
4599 1.1 christos }
4600 1.1 christos
4601 1.1 christos static isc_result_t
4602 1.1 christos find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
4603 1.1 christos isc_stdtime_t now, dns_name_t *foundname,
4604 1.1 christos dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
4605 1.1 christos {
4606 1.1 christos dns_rbtnode_t *node;
4607 1.1 christos rdatasetheader_t *header, *header_next, *header_prev;
4608 1.1 christos rdatasetheader_t *found, *foundsig;
4609 1.3 christos bool empty_node;
4610 1.1 christos isc_result_t result;
4611 1.1 christos dns_fixedname_t fname, forigin;
4612 1.1 christos dns_name_t *name, *origin;
4613 1.1 christos rbtdb_rdatatype_t matchtype, sigmatchtype;
4614 1.1 christos nodelock_t *lock;
4615 1.1 christos isc_rwlocktype_t locktype;
4616 1.1 christos dns_rbtnodechain_t chain;
4617 1.1 christos
4618 1.1 christos chain = search->chain;
4619 1.1 christos
4620 1.1 christos matchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_nsec, 0);
4621 1.1 christos sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig,
4622 1.1 christos dns_rdatatype_nsec);
4623 1.1 christos
4624 1.1 christos do {
4625 1.1 christos node = NULL;
4626 1.1 christos name = dns_fixedname_initname(&fname);
4627 1.1 christos origin = dns_fixedname_initname(&forigin);
4628 1.1 christos result = dns_rbtnodechain_current(&chain, name, origin, &node);
4629 1.1 christos if (result != ISC_R_SUCCESS)
4630 1.1 christos return (result);
4631 1.1 christos locktype = isc_rwlocktype_read;
4632 1.1 christos lock = &(search->rbtdb->node_locks[node->locknum].lock);
4633 1.1 christos NODE_LOCK(lock, locktype);
4634 1.1 christos found = NULL;
4635 1.1 christos foundsig = NULL;
4636 1.3 christos empty_node = true;
4637 1.1 christos header_prev = NULL;
4638 1.1 christos for (header = node->data; header != NULL; header = header_next)
4639 1.1 christos {
4640 1.1 christos header_next = header->next;
4641 1.1 christos if (check_stale_header(node, header,
4642 1.1 christos &locktype, lock, search,
4643 1.1 christos &header_prev)) {
4644 1.1 christos continue;
4645 1.1 christos }
4646 1.1 christos if (NONEXISTENT(header) ||
4647 1.1 christos RBTDB_RDATATYPE_BASE(header->type) == 0) {
4648 1.1 christos header_prev = header;
4649 1.1 christos continue;
4650 1.1 christos }
4651 1.1 christos /*
4652 1.1 christos * Don't stop on provable noqname / RRSIG.
4653 1.1 christos */
4654 1.1 christos if (header->noqname == NULL &&
4655 1.1 christos RBTDB_RDATATYPE_BASE(header->type)
4656 1.1 christos != dns_rdatatype_rrsig)
4657 1.1 christos {
4658 1.3 christos empty_node = false;
4659 1.1 christos }
4660 1.1 christos if (header->type == matchtype)
4661 1.1 christos found = header;
4662 1.1 christos else if (header->type == sigmatchtype)
4663 1.1 christos foundsig = header;
4664 1.1 christos header_prev = header;
4665 1.1 christos }
4666 1.1 christos if (found != NULL) {
4667 1.1 christos result = dns_name_concatenate(name, origin,
4668 1.1 christos foundname, NULL);
4669 1.1 christos if (result != ISC_R_SUCCESS)
4670 1.1 christos goto unlock_node;
4671 1.1 christos bind_rdataset(search->rbtdb, node, found,
4672 1.1 christos now, rdataset);
4673 1.1 christos if (foundsig != NULL)
4674 1.1 christos bind_rdataset(search->rbtdb, node, foundsig,
4675 1.1 christos now, sigrdataset);
4676 1.1 christos new_reference(search->rbtdb, node);
4677 1.1 christos *nodep = node;
4678 1.1 christos result = DNS_R_COVERINGNSEC;
4679 1.1 christos } else if (!empty_node) {
4680 1.1 christos result = ISC_R_NOTFOUND;
4681 1.1 christos } else
4682 1.1 christos result = dns_rbtnodechain_prev(&chain, NULL, NULL);
4683 1.1 christos unlock_node:
4684 1.1 christos NODE_UNLOCK(lock, locktype);
4685 1.1 christos } while (empty_node && result == ISC_R_SUCCESS);
4686 1.1 christos return (result);
4687 1.1 christos }
4688 1.1 christos
4689 1.1 christos static isc_result_t
4690 1.1 christos cache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
4691 1.1 christos dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
4692 1.1 christos dns_dbnode_t **nodep, dns_name_t *foundname,
4693 1.1 christos dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
4694 1.1 christos {
4695 1.1 christos dns_rbtnode_t *node = NULL;
4696 1.1 christos isc_result_t result;
4697 1.1 christos rbtdb_search_t search;
4698 1.3 christos bool cname_ok = true;
4699 1.3 christos bool empty_node;
4700 1.1 christos nodelock_t *lock;
4701 1.1 christos isc_rwlocktype_t locktype;
4702 1.1 christos rdatasetheader_t *header, *header_prev, *header_next;
4703 1.1 christos rdatasetheader_t *found, *nsheader;
4704 1.1 christos rdatasetheader_t *foundsig, *nssig, *cnamesig;
4705 1.1 christos rdatasetheader_t *update, *updatesig;
4706 1.1 christos rdatasetheader_t *nsecheader, *nsecsig;
4707 1.1 christos rbtdb_rdatatype_t sigtype, negtype;
4708 1.1 christos
4709 1.1 christos UNUSED(version);
4710 1.1 christos
4711 1.1 christos search.rbtdb = (dns_rbtdb_t *)db;
4712 1.1 christos
4713 1.1 christos REQUIRE(VALID_RBTDB(search.rbtdb));
4714 1.1 christos REQUIRE(version == NULL);
4715 1.1 christos
4716 1.1 christos if (now == 0)
4717 1.1 christos isc_stdtime_get(&now);
4718 1.1 christos
4719 1.1 christos search.rbtversion = NULL;
4720 1.1 christos search.serial = 1;
4721 1.1 christos search.options = options;
4722 1.3 christos search.copy_name = false;
4723 1.3 christos search.need_cleanup = false;
4724 1.3 christos search.wild = false;
4725 1.1 christos search.zonecut = NULL;
4726 1.1 christos dns_fixedname_init(&search.zonecut_name);
4727 1.1 christos dns_rbtnodechain_init(&search.chain, search.rbtdb->common.mctx);
4728 1.1 christos search.now = now;
4729 1.1 christos update = NULL;
4730 1.1 christos updatesig = NULL;
4731 1.1 christos
4732 1.1 christos RWLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read);
4733 1.1 christos
4734 1.1 christos /*
4735 1.1 christos * Search down from the root of the tree. If, while going down, we
4736 1.1 christos * encounter a callback node, cache_zonecut_callback() will search the
4737 1.1 christos * rdatasets at the zone cut for a DNAME rdataset.
4738 1.1 christos */
4739 1.1 christos result = dns_rbt_findnode(search.rbtdb->tree, name, foundname, &node,
4740 1.1 christos &search.chain, DNS_RBTFIND_EMPTYDATA,
4741 1.1 christos cache_zonecut_callback, &search);
4742 1.1 christos
4743 1.1 christos if (result == DNS_R_PARTIALMATCH) {
4744 1.1 christos if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0) {
4745 1.1 christos result = find_coveringnsec(&search, nodep, now,
4746 1.1 christos foundname, rdataset,
4747 1.1 christos sigrdataset);
4748 1.1 christos if (result == DNS_R_COVERINGNSEC)
4749 1.1 christos goto tree_exit;
4750 1.1 christos }
4751 1.1 christos if (search.zonecut != NULL) {
4752 1.1 christos result = setup_delegation(&search, nodep, foundname,
4753 1.1 christos rdataset, sigrdataset);
4754 1.1 christos goto tree_exit;
4755 1.1 christos } else {
4756 1.1 christos find_ns:
4757 1.1 christos result = find_deepest_zonecut(&search, node, nodep,
4758 1.1 christos foundname, rdataset,
4759 1.1 christos sigrdataset);
4760 1.1 christos goto tree_exit;
4761 1.1 christos }
4762 1.1 christos } else if (result != ISC_R_SUCCESS)
4763 1.1 christos goto tree_exit;
4764 1.1 christos
4765 1.1 christos /*
4766 1.1 christos * Certain DNSSEC types are not subject to CNAME matching
4767 1.1 christos * (RFC4035, section 2.5 and RFC3007).
4768 1.1 christos *
4769 1.1 christos * We don't check for RRSIG, because we don't store RRSIG records
4770 1.1 christos * directly.
4771 1.1 christos */
4772 1.1 christos if (type == dns_rdatatype_key || type == dns_rdatatype_nsec)
4773 1.3 christos cname_ok = false;
4774 1.1 christos
4775 1.1 christos /*
4776 1.1 christos * We now go looking for rdata...
4777 1.1 christos */
4778 1.1 christos
4779 1.1 christos lock = &(search.rbtdb->node_locks[node->locknum].lock);
4780 1.1 christos locktype = isc_rwlocktype_read;
4781 1.1 christos NODE_LOCK(lock, locktype);
4782 1.1 christos
4783 1.1 christos found = NULL;
4784 1.1 christos foundsig = NULL;
4785 1.1 christos sigtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type);
4786 1.1 christos negtype = RBTDB_RDATATYPE_VALUE(0, type);
4787 1.1 christos nsheader = NULL;
4788 1.1 christos nsecheader = NULL;
4789 1.1 christos nssig = NULL;
4790 1.1 christos nsecsig = NULL;
4791 1.1 christos cnamesig = NULL;
4792 1.3 christos empty_node = true;
4793 1.1 christos header_prev = NULL;
4794 1.1 christos for (header = node->data; header != NULL; header = header_next) {
4795 1.1 christos header_next = header->next;
4796 1.1 christos if (check_stale_header(node, header,
4797 1.1 christos &locktype, lock, &search,
4798 1.1 christos &header_prev)) {
4799 1.1 christos /* Do nothing. */
4800 1.1 christos } else if (EXISTS(header) && !ANCIENT(header)) {
4801 1.1 christos /*
4802 1.1 christos * We now know that there is at least one active
4803 1.1 christos * non-stale rdataset at this node.
4804 1.1 christos */
4805 1.3 christos empty_node = false;
4806 1.1 christos
4807 1.1 christos /*
4808 1.1 christos * If we found a type we were looking for, remember
4809 1.1 christos * it.
4810 1.1 christos */
4811 1.1 christos if (header->type == type ||
4812 1.1 christos (type == dns_rdatatype_any &&
4813 1.1 christos RBTDB_RDATATYPE_BASE(header->type) != 0) ||
4814 1.1 christos (cname_ok && header->type ==
4815 1.1 christos dns_rdatatype_cname)) {
4816 1.1 christos /*
4817 1.1 christos * We've found the answer.
4818 1.1 christos */
4819 1.1 christos found = header;
4820 1.1 christos if (header->type == dns_rdatatype_cname &&
4821 1.1 christos cname_ok &&
4822 1.1 christos cnamesig != NULL) {
4823 1.1 christos /*
4824 1.1 christos * If we've already got the
4825 1.1 christos * CNAME RRSIG, use it.
4826 1.1 christos */
4827 1.1 christos foundsig = cnamesig;
4828 1.1 christos }
4829 1.1 christos } else if (header->type == sigtype) {
4830 1.1 christos /*
4831 1.1 christos * We've found the RRSIG rdataset for our
4832 1.1 christos * target type. Remember it.
4833 1.1 christos */
4834 1.1 christos foundsig = header;
4835 1.1 christos } else if (header->type == RBTDB_RDATATYPE_NCACHEANY ||
4836 1.1 christos header->type == negtype) {
4837 1.1 christos /*
4838 1.1 christos * We've found a negative cache entry.
4839 1.1 christos */
4840 1.1 christos found = header;
4841 1.1 christos } else if (header->type == dns_rdatatype_ns) {
4842 1.1 christos /*
4843 1.1 christos * Remember a NS rdataset even if we're
4844 1.1 christos * not specifically looking for it, because
4845 1.1 christos * we might need it later.
4846 1.1 christos */
4847 1.1 christos nsheader = header;
4848 1.1 christos } else if (header->type == RBTDB_RDATATYPE_SIGNS) {
4849 1.1 christos /*
4850 1.1 christos * If we need the NS rdataset, we'll also
4851 1.1 christos * need its signature.
4852 1.1 christos */
4853 1.1 christos nssig = header;
4854 1.1 christos } else if (header->type == dns_rdatatype_nsec) {
4855 1.1 christos nsecheader = header;
4856 1.1 christos } else if (header->type == RBTDB_RDATATYPE_SIGNSEC) {
4857 1.1 christos nsecsig = header;
4858 1.1 christos } else if (cname_ok &&
4859 1.1 christos header->type == RBTDB_RDATATYPE_SIGCNAME) {
4860 1.1 christos /*
4861 1.1 christos * If we get a CNAME match, we'll also need
4862 1.1 christos * its signature.
4863 1.1 christos */
4864 1.1 christos cnamesig = header;
4865 1.1 christos }
4866 1.1 christos header_prev = header;
4867 1.1 christos } else
4868 1.1 christos header_prev = header;
4869 1.1 christos }
4870 1.1 christos
4871 1.1 christos if (empty_node) {
4872 1.1 christos /*
4873 1.1 christos * We have an exact match for the name, but there are no
4874 1.1 christos * extant rdatasets. That means that this node doesn't
4875 1.1 christos * meaningfully exist, and that we really have a partial match.
4876 1.1 christos */
4877 1.1 christos NODE_UNLOCK(lock, locktype);
4878 1.1 christos goto find_ns;
4879 1.1 christos }
4880 1.1 christos
4881 1.1 christos /*
4882 1.1 christos * If we didn't find what we were looking for...
4883 1.1 christos */
4884 1.1 christos if (found == NULL ||
4885 1.1 christos (DNS_TRUST_ADDITIONAL(found->trust) &&
4886 1.1 christos ((options & DNS_DBFIND_ADDITIONALOK) == 0)) ||
4887 1.1 christos (found->trust == dns_trust_glue &&
4888 1.1 christos ((options & DNS_DBFIND_GLUEOK) == 0)) ||
4889 1.1 christos (DNS_TRUST_PENDING(found->trust) &&
4890 1.1 christos ((options & DNS_DBFIND_PENDINGOK) == 0))) {
4891 1.1 christos
4892 1.1 christos /*
4893 1.1 christos * Return covering NODATA NSEC record.
4894 1.1 christos */
4895 1.1 christos if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0 &&
4896 1.1 christos nsecheader != NULL)
4897 1.1 christos {
4898 1.1 christos if (nodep != NULL) {
4899 1.1 christos new_reference(search.rbtdb, node);
4900 1.1 christos INSIST(!ISC_LINK_LINKED(node, deadlink));
4901 1.1 christos *nodep = node;
4902 1.1 christos }
4903 1.1 christos bind_rdataset(search.rbtdb, node, nsecheader,
4904 1.1 christos search.now, rdataset);
4905 1.1 christos if (need_headerupdate(nsecheader, search.now))
4906 1.1 christos update = nsecheader;
4907 1.1 christos if (nsecsig != NULL) {
4908 1.1 christos bind_rdataset(search.rbtdb, node, nsecsig,
4909 1.1 christos search.now, sigrdataset);
4910 1.1 christos if (need_headerupdate(nsecsig, search.now))
4911 1.1 christos updatesig = nsecsig;
4912 1.1 christos }
4913 1.1 christos result = DNS_R_COVERINGNSEC;
4914 1.1 christos goto node_exit;
4915 1.1 christos }
4916 1.1 christos
4917 1.1 christos /*
4918 1.1 christos * If there is an NS rdataset at this node, then this is the
4919 1.1 christos * deepest zone cut.
4920 1.1 christos */
4921 1.1 christos if (nsheader != NULL) {
4922 1.1 christos if (nodep != NULL) {
4923 1.1 christos new_reference(search.rbtdb, node);
4924 1.1 christos INSIST(!ISC_LINK_LINKED(node, deadlink));
4925 1.1 christos *nodep = node;
4926 1.1 christos }
4927 1.1 christos bind_rdataset(search.rbtdb, node, nsheader, search.now,
4928 1.1 christos rdataset);
4929 1.1 christos if (need_headerupdate(nsheader, search.now))
4930 1.1 christos update = nsheader;
4931 1.1 christos if (nssig != NULL) {
4932 1.1 christos bind_rdataset(search.rbtdb, node, nssig,
4933 1.1 christos search.now, sigrdataset);
4934 1.1 christos if (need_headerupdate(nssig, search.now))
4935 1.1 christos updatesig = nssig;
4936 1.1 christos }
4937 1.1 christos result = DNS_R_DELEGATION;
4938 1.1 christos goto node_exit;
4939 1.1 christos }
4940 1.1 christos
4941 1.1 christos /*
4942 1.1 christos * Go find the deepest zone cut.
4943 1.1 christos */
4944 1.1 christos NODE_UNLOCK(lock, locktype);
4945 1.1 christos goto find_ns;
4946 1.1 christos }
4947 1.1 christos
4948 1.1 christos /*
4949 1.1 christos * We found what we were looking for, or we found a CNAME.
4950 1.1 christos */
4951 1.1 christos
4952 1.1 christos if (nodep != NULL) {
4953 1.1 christos new_reference(search.rbtdb, node);
4954 1.1 christos INSIST(!ISC_LINK_LINKED(node, deadlink));
4955 1.1 christos *nodep = node;
4956 1.1 christos }
4957 1.1 christos
4958 1.1 christos if (NEGATIVE(found)) {
4959 1.1 christos /*
4960 1.1 christos * We found a negative cache entry.
4961 1.1 christos */
4962 1.1 christos if (NXDOMAIN(found))
4963 1.1 christos result = DNS_R_NCACHENXDOMAIN;
4964 1.1 christos else
4965 1.1 christos result = DNS_R_NCACHENXRRSET;
4966 1.1 christos } else if (type != found->type &&
4967 1.1 christos type != dns_rdatatype_any &&
4968 1.1 christos found->type == dns_rdatatype_cname) {
4969 1.1 christos /*
4970 1.1 christos * We weren't doing an ANY query and we found a CNAME instead
4971 1.1 christos * of the type we were looking for, so we need to indicate
4972 1.1 christos * that result to the caller.
4973 1.1 christos */
4974 1.1 christos result = DNS_R_CNAME;
4975 1.1 christos } else {
4976 1.1 christos /*
4977 1.1 christos * An ordinary successful query!
4978 1.1 christos */
4979 1.1 christos result = ISC_R_SUCCESS;
4980 1.1 christos }
4981 1.1 christos
4982 1.1 christos if (type != dns_rdatatype_any || result == DNS_R_NCACHENXDOMAIN ||
4983 1.1 christos result == DNS_R_NCACHENXRRSET) {
4984 1.1 christos bind_rdataset(search.rbtdb, node, found, search.now,
4985 1.1 christos rdataset);
4986 1.1 christos if (need_headerupdate(found, search.now))
4987 1.1 christos update = found;
4988 1.1 christos if (!NEGATIVE(found) && foundsig != NULL) {
4989 1.1 christos bind_rdataset(search.rbtdb, node, foundsig, search.now,
4990 1.1 christos sigrdataset);
4991 1.1 christos if (need_headerupdate(foundsig, search.now))
4992 1.1 christos updatesig = foundsig;
4993 1.1 christos }
4994 1.1 christos }
4995 1.1 christos
4996 1.1 christos node_exit:
4997 1.1 christos if ((update != NULL || updatesig != NULL) &&
4998 1.1 christos locktype != isc_rwlocktype_write) {
4999 1.1 christos NODE_UNLOCK(lock, locktype);
5000 1.1 christos NODE_LOCK(lock, isc_rwlocktype_write);
5001 1.1 christos locktype = isc_rwlocktype_write;
5002 1.1 christos POST(locktype);
5003 1.1 christos }
5004 1.1 christos if (update != NULL && need_headerupdate(update, search.now))
5005 1.1 christos update_header(search.rbtdb, update, search.now);
5006 1.1 christos if (updatesig != NULL && need_headerupdate(updatesig, search.now))
5007 1.1 christos update_header(search.rbtdb, updatesig, search.now);
5008 1.1 christos
5009 1.1 christos NODE_UNLOCK(lock, locktype);
5010 1.1 christos
5011 1.1 christos tree_exit:
5012 1.1 christos RWUNLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read);
5013 1.1 christos
5014 1.1 christos /*
5015 1.1 christos * If we found a zonecut but aren't going to use it, we have to
5016 1.1 christos * let go of it.
5017 1.1 christos */
5018 1.1 christos if (search.need_cleanup) {
5019 1.1 christos node = search.zonecut;
5020 1.1 christos INSIST(node != NULL);
5021 1.1 christos lock = &(search.rbtdb->node_locks[node->locknum].lock);
5022 1.1 christos
5023 1.1 christos NODE_LOCK(lock, isc_rwlocktype_read);
5024 1.1 christos decrement_reference(search.rbtdb, node, 0,
5025 1.1 christos isc_rwlocktype_read, isc_rwlocktype_none,
5026 1.3 christos false);
5027 1.1 christos NODE_UNLOCK(lock, isc_rwlocktype_read);
5028 1.1 christos }
5029 1.1 christos
5030 1.1 christos dns_rbtnodechain_reset(&search.chain);
5031 1.1 christos
5032 1.1 christos update_cachestats(search.rbtdb, result);
5033 1.1 christos return (result);
5034 1.1 christos }
5035 1.1 christos
5036 1.1 christos static isc_result_t
5037 1.1 christos cache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options,
5038 1.1 christos isc_stdtime_t now, dns_dbnode_t **nodep,
5039 1.3 christos dns_name_t *foundname, dns_name_t *dcname,
5040 1.1 christos dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
5041 1.1 christos {
5042 1.1 christos dns_rbtnode_t *node = NULL;
5043 1.1 christos nodelock_t *lock;
5044 1.1 christos isc_result_t result;
5045 1.1 christos rbtdb_search_t search;
5046 1.1 christos rdatasetheader_t *header, *header_prev, *header_next;
5047 1.1 christos rdatasetheader_t *found, *foundsig;
5048 1.1 christos unsigned int rbtoptions = DNS_RBTFIND_EMPTYDATA;
5049 1.1 christos isc_rwlocktype_t locktype;
5050 1.3 christos bool dcnull = (dcname == NULL);
5051 1.1 christos
5052 1.1 christos search.rbtdb = (dns_rbtdb_t *)db;
5053 1.1 christos
5054 1.1 christos REQUIRE(VALID_RBTDB(search.rbtdb));
5055 1.1 christos
5056 1.1 christos if (now == 0)
5057 1.1 christos isc_stdtime_get(&now);
5058 1.1 christos
5059 1.1 christos search.rbtversion = NULL;
5060 1.1 christos search.serial = 1;
5061 1.1 christos search.options = options;
5062 1.3 christos search.copy_name = false;
5063 1.3 christos search.need_cleanup = false;
5064 1.3 christos search.wild = false;
5065 1.1 christos search.zonecut = NULL;
5066 1.1 christos dns_fixedname_init(&search.zonecut_name);
5067 1.1 christos dns_rbtnodechain_init(&search.chain, search.rbtdb->common.mctx);
5068 1.1 christos search.now = now;
5069 1.1 christos
5070 1.3 christos if (dcnull) {
5071 1.3 christos dcname = foundname;
5072 1.3 christos }
5073 1.3 christos
5074 1.1 christos if ((options & DNS_DBFIND_NOEXACT) != 0)
5075 1.1 christos rbtoptions |= DNS_RBTFIND_NOEXACT;
5076 1.1 christos
5077 1.1 christos RWLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read);
5078 1.1 christos
5079 1.1 christos /*
5080 1.1 christos * Search down from the root of the tree.
5081 1.1 christos */
5082 1.3 christos result = dns_rbt_findnode(search.rbtdb->tree, name, dcname, &node,
5083 1.1 christos &search.chain, rbtoptions, NULL, &search);
5084 1.1 christos
5085 1.1 christos if (result == DNS_R_PARTIALMATCH) {
5086 1.1 christos result = find_deepest_zonecut(&search, node, nodep, foundname,
5087 1.1 christos rdataset, sigrdataset);
5088 1.1 christos goto tree_exit;
5089 1.3 christos } else if (result != ISC_R_SUCCESS) {
5090 1.1 christos goto tree_exit;
5091 1.3 christos } else if (!dcnull) {
5092 1.3 christos dns_name_copy(dcname, foundname, NULL);
5093 1.3 christos }
5094 1.1 christos /*
5095 1.1 christos * We now go looking for an NS rdataset at the node.
5096 1.1 christos */
5097 1.1 christos
5098 1.1 christos lock = &(search.rbtdb->node_locks[node->locknum].lock);
5099 1.1 christos locktype = isc_rwlocktype_read;
5100 1.1 christos NODE_LOCK(lock, locktype);
5101 1.1 christos
5102 1.1 christos found = NULL;
5103 1.1 christos foundsig = NULL;
5104 1.1 christos header_prev = NULL;
5105 1.1 christos for (header = node->data; header != NULL; header = header_next) {
5106 1.1 christos header_next = header->next;
5107 1.1 christos if (check_stale_header(node, header,
5108 1.1 christos &locktype, lock, &search,
5109 1.1 christos &header_prev)) {
5110 1.1 christos /* Do nothing. */
5111 1.1 christos } else if (EXISTS(header)) {
5112 1.1 christos /*
5113 1.1 christos * If we found a type we were looking for, remember
5114 1.1 christos * it.
5115 1.1 christos */
5116 1.1 christos if (header->type == dns_rdatatype_ns) {
5117 1.1 christos /*
5118 1.1 christos * Remember a NS rdataset even if we're
5119 1.1 christos * not specifically looking for it, because
5120 1.1 christos * we might need it later.
5121 1.1 christos */
5122 1.1 christos found = header;
5123 1.1 christos } else if (header->type == RBTDB_RDATATYPE_SIGNS) {
5124 1.1 christos /*
5125 1.1 christos * If we need the NS rdataset, we'll also
5126 1.1 christos * need its signature.
5127 1.1 christos */
5128 1.1 christos foundsig = header;
5129 1.1 christos }
5130 1.1 christos header_prev = header;
5131 1.1 christos } else
5132 1.1 christos header_prev = header;
5133 1.1 christos }
5134 1.1 christos
5135 1.1 christos if (found == NULL) {
5136 1.1 christos /*
5137 1.1 christos * No NS records here.
5138 1.1 christos */
5139 1.1 christos NODE_UNLOCK(lock, locktype);
5140 1.3 christos result = find_deepest_zonecut(&search, node, nodep, foundname,
5141 1.3 christos rdataset, sigrdataset);
5142 1.3 christos goto tree_exit;
5143 1.1 christos }
5144 1.1 christos
5145 1.1 christos if (nodep != NULL) {
5146 1.1 christos new_reference(search.rbtdb, node);
5147 1.1 christos INSIST(!ISC_LINK_LINKED(node, deadlink));
5148 1.1 christos *nodep = node;
5149 1.1 christos }
5150 1.1 christos
5151 1.1 christos bind_rdataset(search.rbtdb, node, found, search.now, rdataset);
5152 1.1 christos if (foundsig != NULL)
5153 1.1 christos bind_rdataset(search.rbtdb, node, foundsig, search.now,
5154 1.1 christos sigrdataset);
5155 1.1 christos
5156 1.1 christos if (need_headerupdate(found, search.now) ||
5157 1.1 christos (foundsig != NULL && need_headerupdate(foundsig, search.now))) {
5158 1.1 christos if (locktype != isc_rwlocktype_write) {
5159 1.1 christos NODE_UNLOCK(lock, locktype);
5160 1.1 christos NODE_LOCK(lock, isc_rwlocktype_write);
5161 1.1 christos locktype = isc_rwlocktype_write;
5162 1.1 christos POST(locktype);
5163 1.1 christos }
5164 1.1 christos if (need_headerupdate(found, search.now))
5165 1.1 christos update_header(search.rbtdb, found, search.now);
5166 1.1 christos if (foundsig != NULL &&
5167 1.1 christos need_headerupdate(foundsig, search.now)) {
5168 1.1 christos update_header(search.rbtdb, foundsig, search.now);
5169 1.1 christos }
5170 1.1 christos }
5171 1.1 christos
5172 1.1 christos NODE_UNLOCK(lock, locktype);
5173 1.1 christos
5174 1.1 christos tree_exit:
5175 1.1 christos RWUNLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read);
5176 1.1 christos
5177 1.1 christos INSIST(!search.need_cleanup);
5178 1.1 christos
5179 1.1 christos dns_rbtnodechain_reset(&search.chain);
5180 1.1 christos
5181 1.1 christos if (result == DNS_R_DELEGATION)
5182 1.1 christos result = ISC_R_SUCCESS;
5183 1.1 christos
5184 1.1 christos return (result);
5185 1.1 christos }
5186 1.1 christos
5187 1.1 christos static void
5188 1.1 christos attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) {
5189 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
5190 1.1 christos dns_rbtnode_t *node = (dns_rbtnode_t *)source;
5191 1.1 christos
5192 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
5193 1.1 christos REQUIRE(targetp != NULL && *targetp == NULL);
5194 1.1 christos
5195 1.3 christos isc_refcount_increment(&node->references);
5196 1.1 christos
5197 1.1 christos *targetp = source;
5198 1.1 christos }
5199 1.1 christos
5200 1.1 christos static void
5201 1.1 christos detachnode(dns_db_t *db, dns_dbnode_t **targetp) {
5202 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
5203 1.1 christos dns_rbtnode_t *node;
5204 1.3 christos bool want_free = false;
5205 1.3 christos bool inactive = false;
5206 1.1 christos rbtdb_nodelock_t *nodelock;
5207 1.1 christos
5208 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
5209 1.1 christos REQUIRE(targetp != NULL && *targetp != NULL);
5210 1.1 christos
5211 1.1 christos node = (dns_rbtnode_t *)(*targetp);
5212 1.1 christos nodelock = &rbtdb->node_locks[node->locknum];
5213 1.1 christos
5214 1.1 christos NODE_LOCK(&nodelock->lock, isc_rwlocktype_read);
5215 1.1 christos
5216 1.1 christos if (decrement_reference(rbtdb, node, 0, isc_rwlocktype_read,
5217 1.3 christos isc_rwlocktype_none, false)) {
5218 1.1 christos if (isc_refcount_current(&nodelock->references) == 0 &&
5219 1.1 christos nodelock->exiting) {
5220 1.3 christos inactive = true;
5221 1.1 christos }
5222 1.1 christos }
5223 1.1 christos
5224 1.1 christos NODE_UNLOCK(&nodelock->lock, isc_rwlocktype_read);
5225 1.1 christos
5226 1.1 christos *targetp = NULL;
5227 1.1 christos
5228 1.1 christos if (inactive) {
5229 1.1 christos RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
5230 1.1 christos rbtdb->active--;
5231 1.1 christos if (rbtdb->active == 0)
5232 1.3 christos want_free = true;
5233 1.1 christos RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
5234 1.1 christos if (want_free) {
5235 1.1 christos char buf[DNS_NAME_FORMATSIZE];
5236 1.1 christos if (dns_name_dynamic(&rbtdb->common.origin))
5237 1.1 christos dns_name_format(&rbtdb->common.origin, buf,
5238 1.1 christos sizeof(buf));
5239 1.1 christos else
5240 1.1 christos strlcpy(buf, "<UNKNOWN>", sizeof(buf));
5241 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
5242 1.1 christos DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
5243 1.1 christos "calling free_rbtdb(%s)", buf);
5244 1.3 christos free_rbtdb(rbtdb, true, NULL);
5245 1.1 christos }
5246 1.1 christos }
5247 1.1 christos }
5248 1.1 christos
5249 1.1 christos static isc_result_t
5250 1.1 christos expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) {
5251 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
5252 1.1 christos dns_rbtnode_t *rbtnode = node;
5253 1.1 christos rdatasetheader_t *header;
5254 1.3 christos bool force_expire = false;
5255 1.1 christos /*
5256 1.1 christos * These are the category and module used by the cache cleaner.
5257 1.1 christos */
5258 1.3 christos bool log = false;
5259 1.1 christos isc_logcategory_t *category = DNS_LOGCATEGORY_DATABASE;
5260 1.1 christos isc_logmodule_t *module = DNS_LOGMODULE_CACHE;
5261 1.1 christos int level = ISC_LOG_DEBUG(2);
5262 1.1 christos char printname[DNS_NAME_FORMATSIZE];
5263 1.1 christos
5264 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
5265 1.1 christos
5266 1.1 christos /*
5267 1.1 christos * Caller must hold a tree lock.
5268 1.1 christos */
5269 1.1 christos
5270 1.1 christos if (now == 0)
5271 1.1 christos isc_stdtime_get(&now);
5272 1.1 christos
5273 1.1 christos if (isc_mem_isovermem(rbtdb->common.mctx)) {
5274 1.1 christos /*
5275 1.3 christos * Force expire with 25% probability.
5276 1.1 christos * XXXDCL Could stand to have a better policy, like LRU.
5277 1.1 christos */
5278 1.3 christos force_expire = (rbtnode->down == NULL &&
5279 1.3 christos (isc_random32() % 4) == 0);
5280 1.1 christos
5281 1.1 christos /*
5282 1.1 christos * Note that 'log' can be true IFF overmem is also true.
5283 1.1 christos * overmem can currently only be true for cache
5284 1.1 christos * databases -- hence all of the "overmem cache" log strings.
5285 1.1 christos */
5286 1.3 christos log = isc_log_wouldlog(dns_lctx, level);
5287 1.1 christos if (log)
5288 1.1 christos isc_log_write(dns_lctx, category, module, level,
5289 1.1 christos "overmem cache: %s %s",
5290 1.1 christos force_expire ? "FORCE" : "check",
5291 1.1 christos dns_rbt_formatnodename(rbtnode,
5292 1.1 christos printname,
5293 1.1 christos sizeof(printname)));
5294 1.1 christos }
5295 1.1 christos
5296 1.1 christos /*
5297 1.1 christos * We may not need write access, but this code path is not performance
5298 1.1 christos * sensitive, so it should be okay to always lock as a writer.
5299 1.1 christos */
5300 1.1 christos NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
5301 1.1 christos isc_rwlocktype_write);
5302 1.1 christos
5303 1.1 christos for (header = rbtnode->data; header != NULL; header = header->next)
5304 1.1 christos if (header->rdh_ttl <= now - RBTDB_VIRTUAL) {
5305 1.1 christos /*
5306 1.1 christos * We don't check if refcurrent(rbtnode) == 0 and try
5307 1.1 christos * to free like we do in cache_find(), because
5308 1.1 christos * refcurrent(rbtnode) must be non-zero. This is so
5309 1.1 christos * because 'node' is an argument to the function.
5310 1.1 christos */
5311 1.1 christos mark_header_ancient(rbtdb, header);
5312 1.1 christos if (log)
5313 1.1 christos isc_log_write(dns_lctx, category, module,
5314 1.1 christos level, "overmem cache: stale %s",
5315 1.1 christos printname);
5316 1.1 christos } else if (force_expire) {
5317 1.1 christos if (! RETAIN(header)) {
5318 1.1 christos set_ttl(rbtdb, header, 0);
5319 1.1 christos mark_header_ancient(rbtdb, header);
5320 1.1 christos } else if (log) {
5321 1.1 christos isc_log_write(dns_lctx, category, module,
5322 1.1 christos level, "overmem cache: "
5323 1.1 christos "reprieve by RETAIN() %s",
5324 1.1 christos printname);
5325 1.1 christos }
5326 1.1 christos } else if (isc_mem_isovermem(rbtdb->common.mctx) && log)
5327 1.1 christos isc_log_write(dns_lctx, category, module, level,
5328 1.1 christos "overmem cache: saved %s", printname);
5329 1.1 christos
5330 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
5331 1.1 christos isc_rwlocktype_write);
5332 1.1 christos
5333 1.1 christos return (ISC_R_SUCCESS);
5334 1.1 christos }
5335 1.1 christos
5336 1.1 christos static void
5337 1.3 christos overmem(dns_db_t *db, bool over) {
5338 1.1 christos /* This is an empty callback. See adb.c:water() */
5339 1.1 christos
5340 1.1 christos UNUSED(db);
5341 1.1 christos UNUSED(over);
5342 1.1 christos
5343 1.1 christos return;
5344 1.1 christos }
5345 1.1 christos
5346 1.1 christos static void
5347 1.1 christos printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) {
5348 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
5349 1.1 christos dns_rbtnode_t *rbtnode = node;
5350 1.3 christos bool first;
5351 1.3 christos uint32_t refs;
5352 1.1 christos
5353 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
5354 1.1 christos
5355 1.1 christos NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
5356 1.1 christos isc_rwlocktype_read);
5357 1.1 christos
5358 1.3 christos refs = isc_refcount_current(&rbtnode->references);
5359 1.3 christos fprintf(out, "node %p, %" PRIu32 " references, locknum = %u\n",
5360 1.3 christos rbtnode, refs,
5361 1.1 christos rbtnode->locknum);
5362 1.1 christos if (rbtnode->data != NULL) {
5363 1.1 christos rdatasetheader_t *current, *top_next;
5364 1.1 christos
5365 1.1 christos for (current = rbtnode->data; current != NULL;
5366 1.1 christos current = top_next) {
5367 1.1 christos top_next = current->next;
5368 1.3 christos first = true;
5369 1.1 christos fprintf(out, "\ttype %u", current->type);
5370 1.1 christos do {
5371 1.1 christos if (!first)
5372 1.1 christos fprintf(out, "\t");
5373 1.3 christos first = false;
5374 1.1 christos fprintf(out,
5375 1.1 christos "\tserial = %lu, ttl = %u, "
5376 1.1 christos "trust = %u, attributes = %u, "
5377 1.1 christos "resign = %u\n",
5378 1.1 christos (unsigned long)current->serial,
5379 1.1 christos current->rdh_ttl,
5380 1.1 christos current->trust,
5381 1.1 christos current->attributes,
5382 1.1 christos (current->resign << 1) |
5383 1.1 christos current->resign_lsb);
5384 1.1 christos current = current->down;
5385 1.1 christos } while (current != NULL);
5386 1.1 christos }
5387 1.1 christos } else
5388 1.1 christos fprintf(out, "(empty)\n");
5389 1.1 christos
5390 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
5391 1.1 christos isc_rwlocktype_read);
5392 1.1 christos }
5393 1.1 christos
5394 1.1 christos static isc_result_t
5395 1.1 christos createiterator(dns_db_t *db, unsigned int options, dns_dbiterator_t **iteratorp)
5396 1.1 christos {
5397 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
5398 1.1 christos rbtdb_dbiterator_t *rbtdbiter;
5399 1.1 christos
5400 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
5401 1.1 christos
5402 1.1 christos rbtdbiter = isc_mem_get(rbtdb->common.mctx, sizeof(*rbtdbiter));
5403 1.1 christos if (rbtdbiter == NULL)
5404 1.1 christos return (ISC_R_NOMEMORY);
5405 1.1 christos
5406 1.1 christos rbtdbiter->common.methods = &dbiterator_methods;
5407 1.1 christos rbtdbiter->common.db = NULL;
5408 1.1 christos dns_db_attach(db, &rbtdbiter->common.db);
5409 1.1 christos rbtdbiter->common.relative_names =
5410 1.3 christos ((options & DNS_DB_RELATIVENAMES) != 0);
5411 1.1 christos rbtdbiter->common.magic = DNS_DBITERATOR_MAGIC;
5412 1.3 christos rbtdbiter->common.cleaning = false;
5413 1.3 christos rbtdbiter->paused = true;
5414 1.1 christos rbtdbiter->tree_locked = isc_rwlocktype_none;
5415 1.1 christos rbtdbiter->result = ISC_R_SUCCESS;
5416 1.1 christos dns_fixedname_init(&rbtdbiter->name);
5417 1.1 christos dns_fixedname_init(&rbtdbiter->origin);
5418 1.1 christos rbtdbiter->node = NULL;
5419 1.1 christos rbtdbiter->delcnt = 0;
5420 1.3 christos rbtdbiter->nsec3only = ((options & DNS_DB_NSEC3ONLY) != 0);
5421 1.3 christos rbtdbiter->nonsec3 = ((options & DNS_DB_NONSEC3) != 0);
5422 1.1 christos memset(rbtdbiter->deletions, 0, sizeof(rbtdbiter->deletions));
5423 1.1 christos dns_rbtnodechain_init(&rbtdbiter->chain, db->mctx);
5424 1.1 christos dns_rbtnodechain_init(&rbtdbiter->nsec3chain, db->mctx);
5425 1.1 christos if (rbtdbiter->nsec3only)
5426 1.1 christos rbtdbiter->current = &rbtdbiter->nsec3chain;
5427 1.1 christos else
5428 1.1 christos rbtdbiter->current = &rbtdbiter->chain;
5429 1.1 christos
5430 1.1 christos *iteratorp = (dns_dbiterator_t *)rbtdbiter;
5431 1.1 christos
5432 1.1 christos return (ISC_R_SUCCESS);
5433 1.1 christos }
5434 1.1 christos
5435 1.1 christos static isc_result_t
5436 1.1 christos zone_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
5437 1.1 christos dns_rdatatype_t type, dns_rdatatype_t covers,
5438 1.1 christos isc_stdtime_t now, dns_rdataset_t *rdataset,
5439 1.1 christos dns_rdataset_t *sigrdataset)
5440 1.1 christos {
5441 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
5442 1.1 christos dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
5443 1.1 christos rdatasetheader_t *header, *header_next, *found, *foundsig;
5444 1.1 christos rbtdb_serial_t serial;
5445 1.1 christos rbtdb_version_t *rbtversion = version;
5446 1.3 christos bool close_version = false;
5447 1.1 christos rbtdb_rdatatype_t matchtype, sigmatchtype;
5448 1.1 christos
5449 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
5450 1.1 christos REQUIRE(type != dns_rdatatype_any);
5451 1.1 christos INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb);
5452 1.1 christos
5453 1.1 christos if (rbtversion == NULL) {
5454 1.1 christos currentversion(db, (dns_dbversion_t **) (void *)(&rbtversion));
5455 1.3 christos close_version = true;
5456 1.1 christos }
5457 1.1 christos serial = rbtversion->serial;
5458 1.1 christos now = 0;
5459 1.1 christos
5460 1.1 christos NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
5461 1.1 christos isc_rwlocktype_read);
5462 1.1 christos
5463 1.1 christos found = NULL;
5464 1.1 christos foundsig = NULL;
5465 1.1 christos matchtype = RBTDB_RDATATYPE_VALUE(type, covers);
5466 1.1 christos if (covers == 0)
5467 1.1 christos sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type);
5468 1.1 christos else
5469 1.1 christos sigmatchtype = 0;
5470 1.1 christos
5471 1.1 christos for (header = rbtnode->data; header != NULL; header = header_next) {
5472 1.1 christos header_next = header->next;
5473 1.1 christos do {
5474 1.1 christos if (header->serial <= serial &&
5475 1.1 christos !IGNORE(header)) {
5476 1.1 christos /*
5477 1.1 christos * Is this a "this rdataset doesn't
5478 1.1 christos * exist" record?
5479 1.1 christos */
5480 1.1 christos if (NONEXISTENT(header))
5481 1.1 christos header = NULL;
5482 1.1 christos break;
5483 1.1 christos } else
5484 1.1 christos header = header->down;
5485 1.1 christos } while (header != NULL);
5486 1.1 christos if (header != NULL) {
5487 1.1 christos /*
5488 1.1 christos * We have an active, extant rdataset. If it's a
5489 1.1 christos * type we're looking for, remember it.
5490 1.1 christos */
5491 1.1 christos if (header->type == matchtype) {
5492 1.1 christos found = header;
5493 1.1 christos if (foundsig != NULL)
5494 1.1 christos break;
5495 1.1 christos } else if (header->type == sigmatchtype) {
5496 1.1 christos foundsig = header;
5497 1.1 christos if (found != NULL)
5498 1.1 christos break;
5499 1.1 christos }
5500 1.1 christos }
5501 1.1 christos }
5502 1.1 christos if (found != NULL) {
5503 1.1 christos bind_rdataset(rbtdb, rbtnode, found, now, rdataset);
5504 1.1 christos if (foundsig != NULL)
5505 1.1 christos bind_rdataset(rbtdb, rbtnode, foundsig, now,
5506 1.1 christos sigrdataset);
5507 1.1 christos }
5508 1.1 christos
5509 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
5510 1.1 christos isc_rwlocktype_read);
5511 1.1 christos
5512 1.1 christos if (close_version)
5513 1.1 christos closeversion(db, (dns_dbversion_t **) (void *)(&rbtversion),
5514 1.3 christos false);
5515 1.1 christos
5516 1.1 christos if (found == NULL)
5517 1.1 christos return (ISC_R_NOTFOUND);
5518 1.1 christos
5519 1.1 christos return (ISC_R_SUCCESS);
5520 1.1 christos }
5521 1.1 christos
5522 1.1 christos static isc_result_t
5523 1.1 christos cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
5524 1.1 christos dns_rdatatype_t type, dns_rdatatype_t covers,
5525 1.1 christos isc_stdtime_t now, dns_rdataset_t *rdataset,
5526 1.1 christos dns_rdataset_t *sigrdataset)
5527 1.1 christos {
5528 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
5529 1.1 christos dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
5530 1.1 christos rdatasetheader_t *header, *header_next, *found, *foundsig;
5531 1.1 christos rbtdb_rdatatype_t matchtype, sigmatchtype, negtype;
5532 1.1 christos isc_result_t result;
5533 1.1 christos nodelock_t *lock;
5534 1.1 christos isc_rwlocktype_t locktype;
5535 1.1 christos
5536 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
5537 1.1 christos REQUIRE(type != dns_rdatatype_any);
5538 1.1 christos
5539 1.1 christos UNUSED(version);
5540 1.1 christos
5541 1.1 christos result = ISC_R_SUCCESS;
5542 1.1 christos
5543 1.1 christos if (now == 0)
5544 1.1 christos isc_stdtime_get(&now);
5545 1.1 christos
5546 1.1 christos lock = &rbtdb->node_locks[rbtnode->locknum].lock;
5547 1.1 christos locktype = isc_rwlocktype_read;
5548 1.1 christos NODE_LOCK(lock, locktype);
5549 1.1 christos
5550 1.1 christos found = NULL;
5551 1.1 christos foundsig = NULL;
5552 1.1 christos matchtype = RBTDB_RDATATYPE_VALUE(type, covers);
5553 1.1 christos negtype = RBTDB_RDATATYPE_VALUE(0, type);
5554 1.1 christos if (covers == 0)
5555 1.1 christos sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type);
5556 1.1 christos else
5557 1.1 christos sigmatchtype = 0;
5558 1.1 christos
5559 1.1 christos for (header = rbtnode->data; header != NULL; header = header_next) {
5560 1.1 christos header_next = header->next;
5561 1.1 christos if (!ACTIVE(header, now)) {
5562 1.1 christos if ((header->rdh_ttl < now - RBTDB_VIRTUAL) &&
5563 1.1 christos (locktype == isc_rwlocktype_write ||
5564 1.1 christos NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
5565 1.1 christos /*
5566 1.1 christos * We update the node's status only when we
5567 1.1 christos * can get write access.
5568 1.1 christos */
5569 1.1 christos locktype = isc_rwlocktype_write;
5570 1.1 christos
5571 1.1 christos /*
5572 1.1 christos * We don't check if refcurrent(rbtnode) == 0
5573 1.1 christos * and try to free like we do in cache_find(),
5574 1.1 christos * because refcurrent(rbtnode) must be
5575 1.1 christos * non-zero. This is so because 'node' is an
5576 1.1 christos * argument to the function.
5577 1.1 christos */
5578 1.1 christos mark_header_ancient(rbtdb, header);
5579 1.1 christos }
5580 1.1 christos } else if (EXISTS(header) && !ANCIENT(header)) {
5581 1.1 christos if (header->type == matchtype)
5582 1.1 christos found = header;
5583 1.1 christos else if (header->type == RBTDB_RDATATYPE_NCACHEANY ||
5584 1.1 christos header->type == negtype)
5585 1.1 christos found = header;
5586 1.1 christos else if (header->type == sigmatchtype)
5587 1.1 christos foundsig = header;
5588 1.1 christos }
5589 1.1 christos }
5590 1.1 christos if (found != NULL) {
5591 1.1 christos bind_rdataset(rbtdb, rbtnode, found, now, rdataset);
5592 1.1 christos if (!NEGATIVE(found) && foundsig != NULL)
5593 1.1 christos bind_rdataset(rbtdb, rbtnode, foundsig, now,
5594 1.1 christos sigrdataset);
5595 1.1 christos }
5596 1.1 christos
5597 1.1 christos NODE_UNLOCK(lock, locktype);
5598 1.1 christos
5599 1.1 christos if (found == NULL)
5600 1.1 christos return (ISC_R_NOTFOUND);
5601 1.1 christos
5602 1.1 christos if (NEGATIVE(found)) {
5603 1.1 christos /*
5604 1.1 christos * We found a negative cache entry.
5605 1.1 christos */
5606 1.1 christos if (NXDOMAIN(found))
5607 1.1 christos result = DNS_R_NCACHENXDOMAIN;
5608 1.1 christos else
5609 1.1 christos result = DNS_R_NCACHENXRRSET;
5610 1.1 christos }
5611 1.1 christos
5612 1.1 christos update_cachestats(rbtdb, result);
5613 1.1 christos
5614 1.1 christos return (result);
5615 1.1 christos }
5616 1.1 christos
5617 1.1 christos static isc_result_t
5618 1.1 christos allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
5619 1.1 christos isc_stdtime_t now, dns_rdatasetiter_t **iteratorp)
5620 1.1 christos {
5621 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
5622 1.1 christos dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
5623 1.1 christos rbtdb_version_t *rbtversion = version;
5624 1.1 christos rbtdb_rdatasetiter_t *iterator;
5625 1.1 christos
5626 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
5627 1.1 christos
5628 1.1 christos iterator = isc_mem_get(rbtdb->common.mctx, sizeof(*iterator));
5629 1.1 christos if (iterator == NULL)
5630 1.1 christos return (ISC_R_NOMEMORY);
5631 1.1 christos
5632 1.1 christos if ((db->attributes & DNS_DBATTR_CACHE) == 0) {
5633 1.1 christos now = 0;
5634 1.1 christos if (rbtversion == NULL)
5635 1.1 christos currentversion(db,
5636 1.1 christos (dns_dbversion_t **) (void *)(&rbtversion));
5637 1.1 christos else {
5638 1.1 christos INSIST(rbtversion->rbtdb == rbtdb);
5639 1.1 christos
5640 1.3 christos (void)isc_refcount_increment(&rbtversion->references);
5641 1.1 christos }
5642 1.1 christos } else {
5643 1.1 christos if (now == 0)
5644 1.1 christos isc_stdtime_get(&now);
5645 1.1 christos rbtversion = NULL;
5646 1.1 christos }
5647 1.1 christos
5648 1.1 christos iterator->common.magic = DNS_RDATASETITER_MAGIC;
5649 1.1 christos iterator->common.methods = &rdatasetiter_methods;
5650 1.1 christos iterator->common.db = db;
5651 1.1 christos iterator->common.node = node;
5652 1.1 christos iterator->common.version = (dns_dbversion_t *)rbtversion;
5653 1.1 christos iterator->common.now = now;
5654 1.1 christos
5655 1.3 christos isc_refcount_increment(&rbtnode->references);
5656 1.1 christos
5657 1.1 christos iterator->current = NULL;
5658 1.1 christos
5659 1.1 christos *iteratorp = (dns_rdatasetiter_t *)iterator;
5660 1.1 christos
5661 1.1 christos return (ISC_R_SUCCESS);
5662 1.1 christos }
5663 1.1 christos
5664 1.3 christos static bool
5665 1.1 christos cname_and_other_data(dns_rbtnode_t *node, rbtdb_serial_t serial) {
5666 1.1 christos rdatasetheader_t *header, *header_next;
5667 1.3 christos bool cname, other_data;
5668 1.1 christos dns_rdatatype_t rdtype;
5669 1.1 christos
5670 1.1 christos /*
5671 1.1 christos * The caller must hold the node lock.
5672 1.1 christos */
5673 1.1 christos
5674 1.1 christos /*
5675 1.1 christos * Look for CNAME and "other data" rdatasets active in our version.
5676 1.1 christos */
5677 1.3 christos cname = false;
5678 1.3 christos other_data = false;
5679 1.1 christos for (header = node->data; header != NULL; header = header_next) {
5680 1.1 christos header_next = header->next;
5681 1.1 christos if (header->type == dns_rdatatype_cname) {
5682 1.1 christos /*
5683 1.1 christos * Look for an active extant CNAME.
5684 1.1 christos */
5685 1.1 christos do {
5686 1.1 christos if (header->serial <= serial &&
5687 1.1 christos !IGNORE(header)) {
5688 1.1 christos /*
5689 1.1 christos * Is this a "this rdataset doesn't
5690 1.1 christos * exist" record?
5691 1.1 christos */
5692 1.1 christos if (NONEXISTENT(header))
5693 1.1 christos header = NULL;
5694 1.1 christos break;
5695 1.1 christos } else
5696 1.1 christos header = header->down;
5697 1.1 christos } while (header != NULL);
5698 1.1 christos if (header != NULL)
5699 1.3 christos cname = true;
5700 1.1 christos } else {
5701 1.1 christos /*
5702 1.1 christos * Look for active extant "other data".
5703 1.1 christos *
5704 1.1 christos * "Other data" is any rdataset whose type is not
5705 1.1 christos * KEY, NSEC, SIG or RRSIG.
5706 1.1 christos */
5707 1.1 christos rdtype = RBTDB_RDATATYPE_BASE(header->type);
5708 1.1 christos if (rdtype != dns_rdatatype_key &&
5709 1.1 christos rdtype != dns_rdatatype_sig &&
5710 1.1 christos rdtype != dns_rdatatype_nsec &&
5711 1.1 christos rdtype != dns_rdatatype_rrsig) {
5712 1.1 christos /*
5713 1.1 christos * Is it active and extant?
5714 1.1 christos */
5715 1.1 christos do {
5716 1.1 christos if (header->serial <= serial &&
5717 1.1 christos !IGNORE(header)) {
5718 1.1 christos /*
5719 1.1 christos * Is this a "this rdataset
5720 1.1 christos * doesn't exist" record?
5721 1.1 christos */
5722 1.1 christos if (NONEXISTENT(header))
5723 1.1 christos header = NULL;
5724 1.1 christos break;
5725 1.1 christos } else
5726 1.1 christos header = header->down;
5727 1.1 christos } while (header != NULL);
5728 1.1 christos if (header != NULL)
5729 1.3 christos other_data = true;
5730 1.1 christos }
5731 1.1 christos }
5732 1.1 christos }
5733 1.1 christos
5734 1.1 christos if (cname && other_data)
5735 1.3 christos return (true);
5736 1.1 christos
5737 1.3 christos return (false);
5738 1.1 christos }
5739 1.1 christos
5740 1.1 christos static isc_result_t
5741 1.1 christos resign_insert(dns_rbtdb_t *rbtdb, int idx, rdatasetheader_t *newheader) {
5742 1.1 christos isc_result_t result;
5743 1.1 christos
5744 1.1 christos INSIST(!IS_CACHE(rbtdb));
5745 1.1 christos INSIST(newheader->heap_index == 0);
5746 1.1 christos INSIST(!ISC_LINK_LINKED(newheader, link));
5747 1.1 christos
5748 1.1 christos result = isc_heap_insert(rbtdb->heaps[idx], newheader);
5749 1.1 christos return (result);
5750 1.1 christos }
5751 1.1 christos
5752 1.1 christos static void
5753 1.1 christos resign_delete(dns_rbtdb_t *rbtdb, rbtdb_version_t *version,
5754 1.1 christos rdatasetheader_t *header)
5755 1.1 christos {
5756 1.1 christos /*
5757 1.1 christos * Remove the old header from the heap
5758 1.1 christos */
5759 1.1 christos if (header != NULL && header->heap_index != 0) {
5760 1.1 christos isc_heap_delete(rbtdb->heaps[header->node->locknum],
5761 1.1 christos header->heap_index);
5762 1.1 christos header->heap_index = 0;
5763 1.1 christos if (version != NULL) {
5764 1.1 christos new_reference(rbtdb, header->node);
5765 1.1 christos ISC_LIST_APPEND(version->resigned_list, header, link);
5766 1.1 christos }
5767 1.1 christos }
5768 1.1 christos }
5769 1.1 christos
5770 1.1 christos static void
5771 1.3 christos update_recordsandbytes(bool add, rbtdb_version_t *rbtversion,
5772 1.1 christos rdatasetheader_t *header)
5773 1.1 christos {
5774 1.1 christos unsigned char *hdr = (unsigned char *)header;
5775 1.1 christos size_t hdrsize = sizeof (*header);
5776 1.1 christos
5777 1.1 christos if (add) {
5778 1.1 christos rbtversion->records += dns_rdataslab_count(hdr, hdrsize);
5779 1.1 christos rbtversion->bytes += dns_rdataslab_size(hdr, hdrsize);
5780 1.1 christos } else {
5781 1.1 christos rbtversion->records -= dns_rdataslab_count(hdr, hdrsize);
5782 1.1 christos rbtversion->bytes -= dns_rdataslab_size(hdr, hdrsize);
5783 1.1 christos }
5784 1.1 christos }
5785 1.1 christos
5786 1.1 christos static isc_result_t
5787 1.1 christos add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
5788 1.3 christos rdatasetheader_t *newheader, unsigned int options, bool loading,
5789 1.1 christos dns_rdataset_t *addedrdataset, isc_stdtime_t now)
5790 1.1 christos {
5791 1.1 christos rbtdb_changed_t *changed = NULL;
5792 1.1 christos rdatasetheader_t *topheader, *topheader_prev, *header, *sigheader;
5793 1.1 christos unsigned char *merged;
5794 1.1 christos isc_result_t result;
5795 1.3 christos bool header_nx;
5796 1.3 christos bool newheader_nx;
5797 1.3 christos bool merge;
5798 1.1 christos dns_rdatatype_t rdtype, covers;
5799 1.1 christos rbtdb_rdatatype_t negtype, sigtype;
5800 1.1 christos dns_trust_t trust;
5801 1.1 christos int idx;
5802 1.1 christos
5803 1.1 christos /*
5804 1.1 christos * Add an rdatasetheader_t to a node.
5805 1.1 christos */
5806 1.1 christos
5807 1.1 christos /*
5808 1.1 christos * Caller must be holding the node lock.
5809 1.1 christos */
5810 1.1 christos
5811 1.1 christos if ((options & DNS_DBADD_MERGE) != 0) {
5812 1.1 christos REQUIRE(rbtversion != NULL);
5813 1.3 christos merge = true;
5814 1.1 christos } else
5815 1.3 christos merge = false;
5816 1.1 christos
5817 1.1 christos if ((options & DNS_DBADD_FORCE) != 0)
5818 1.1 christos trust = dns_trust_ultimate;
5819 1.1 christos else
5820 1.1 christos trust = newheader->trust;
5821 1.1 christos
5822 1.1 christos if (rbtversion != NULL && !loading) {
5823 1.1 christos /*
5824 1.1 christos * We always add a changed record, even if no changes end up
5825 1.1 christos * being made to this node, because it's harmless and
5826 1.1 christos * simplifies the code.
5827 1.1 christos */
5828 1.1 christos changed = add_changed(rbtdb, rbtversion, rbtnode);
5829 1.1 christos if (changed == NULL) {
5830 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
5831 1.1 christos return (ISC_R_NOMEMORY);
5832 1.1 christos }
5833 1.1 christos }
5834 1.1 christos
5835 1.3 christos newheader_nx = NONEXISTENT(newheader) ? true : false;
5836 1.1 christos topheader_prev = NULL;
5837 1.1 christos sigheader = NULL;
5838 1.1 christos negtype = 0;
5839 1.1 christos if (rbtversion == NULL && !newheader_nx) {
5840 1.1 christos rdtype = RBTDB_RDATATYPE_BASE(newheader->type);
5841 1.1 christos covers = RBTDB_RDATATYPE_EXT(newheader->type);
5842 1.1 christos sigtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, covers);
5843 1.1 christos if (NEGATIVE(newheader)) {
5844 1.1 christos /*
5845 1.1 christos * We're adding a negative cache entry.
5846 1.1 christos */
5847 1.1 christos if (covers == dns_rdatatype_any) {
5848 1.1 christos /*
5849 1.1 christos * If we're adding an negative cache entry
5850 1.1 christos * which covers all types (NXDOMAIN,
5851 1.1 christos * NODATA(QTYPE=ANY)),
5852 1.1 christos *
5853 1.1 christos * We make all other data stale so that the
5854 1.1 christos * only rdataset that can be found at this
5855 1.1 christos * node is the negative cache entry.
5856 1.1 christos */
5857 1.1 christos for (topheader = rbtnode->data;
5858 1.1 christos topheader != NULL;
5859 1.1 christos topheader = topheader->next)
5860 1.1 christos {
5861 1.1 christos set_ttl(rbtdb, topheader, 0);
5862 1.1 christos mark_header_ancient(rbtdb, topheader);
5863 1.1 christos }
5864 1.1 christos goto find_header;
5865 1.1 christos }
5866 1.1 christos /*
5867 1.1 christos * Otherwise look for any RRSIGs of the given
5868 1.1 christos * type so they can be marked stale later.
5869 1.1 christos */
5870 1.1 christos for (topheader = rbtnode->data;
5871 1.1 christos topheader != NULL;
5872 1.1 christos topheader = topheader->next)
5873 1.1 christos if (topheader->type == sigtype)
5874 1.1 christos sigheader = topheader;
5875 1.1 christos negtype = RBTDB_RDATATYPE_VALUE(covers, 0);
5876 1.1 christos } else {
5877 1.1 christos /*
5878 1.1 christos * We're adding something that isn't a
5879 1.1 christos * negative cache entry. Look for an extant
5880 1.1 christos * non-stale NXDOMAIN/NODATA(QTYPE=ANY) negative
5881 1.1 christos * cache entry. If we're adding an RRSIG, also
5882 1.1 christos * check for an extant non-stale NODATA ncache
5883 1.1 christos * entry which covers the same type as the RRSIG.
5884 1.1 christos */
5885 1.1 christos for (topheader = rbtnode->data;
5886 1.1 christos topheader != NULL;
5887 1.1 christos topheader = topheader->next) {
5888 1.1 christos if ((topheader->type ==
5889 1.1 christos RBTDB_RDATATYPE_NCACHEANY) ||
5890 1.1 christos (newheader->type == sigtype &&
5891 1.1 christos topheader->type ==
5892 1.1 christos RBTDB_RDATATYPE_VALUE(0, covers))) {
5893 1.1 christos break;
5894 1.1 christos }
5895 1.1 christos }
5896 1.1 christos if (topheader != NULL && EXISTS(topheader) &&
5897 1.1 christos ACTIVE(topheader, now)) {
5898 1.1 christos /*
5899 1.1 christos * Found one.
5900 1.1 christos */
5901 1.1 christos if (trust < topheader->trust) {
5902 1.1 christos /*
5903 1.1 christos * The NXDOMAIN/NODATA(QTYPE=ANY)
5904 1.1 christos * is more trusted.
5905 1.1 christos */
5906 1.1 christos free_rdataset(rbtdb,
5907 1.1 christos rbtdb->common.mctx,
5908 1.1 christos newheader);
5909 1.1 christos if (addedrdataset != NULL)
5910 1.1 christos bind_rdataset(rbtdb, rbtnode,
5911 1.1 christos topheader, now,
5912 1.1 christos addedrdataset);
5913 1.1 christos return (DNS_R_UNCHANGED);
5914 1.1 christos }
5915 1.1 christos /*
5916 1.1 christos * The new rdataset is better. Expire the
5917 1.1 christos * ncache entry.
5918 1.1 christos */
5919 1.1 christos set_ttl(rbtdb, topheader, 0);
5920 1.1 christos mark_header_ancient(rbtdb, topheader);
5921 1.1 christos topheader = NULL;
5922 1.1 christos goto find_header;
5923 1.1 christos }
5924 1.1 christos negtype = RBTDB_RDATATYPE_VALUE(0, rdtype);
5925 1.1 christos }
5926 1.1 christos }
5927 1.1 christos
5928 1.1 christos for (topheader = rbtnode->data;
5929 1.1 christos topheader != NULL;
5930 1.1 christos topheader = topheader->next) {
5931 1.1 christos if (topheader->type == newheader->type ||
5932 1.1 christos topheader->type == negtype)
5933 1.1 christos break;
5934 1.1 christos topheader_prev = topheader;
5935 1.1 christos }
5936 1.1 christos
5937 1.1 christos find_header:
5938 1.1 christos /*
5939 1.1 christos * If header isn't NULL, we've found the right type. There may be
5940 1.1 christos * IGNORE rdatasets between the top of the chain and the first real
5941 1.1 christos * data. We skip over them.
5942 1.1 christos */
5943 1.1 christos header = topheader;
5944 1.1 christos while (header != NULL && IGNORE(header))
5945 1.1 christos header = header->down;
5946 1.1 christos if (header != NULL) {
5947 1.3 christos header_nx = NONEXISTENT(header) ? true : false;
5948 1.1 christos
5949 1.1 christos /*
5950 1.1 christos * Deleting an already non-existent rdataset has no effect.
5951 1.1 christos */
5952 1.1 christos if (header_nx && newheader_nx) {
5953 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
5954 1.1 christos return (DNS_R_UNCHANGED);
5955 1.1 christos }
5956 1.1 christos
5957 1.1 christos /*
5958 1.1 christos * Trying to add an rdataset with lower trust to a cache
5959 1.1 christos * DB has no effect, provided that the cache data isn't
5960 1.1 christos * stale. If the cache data is stale, new lower trust
5961 1.1 christos * data will supersede it below. Unclear what the best
5962 1.1 christos * policy is here.
5963 1.1 christos */
5964 1.1 christos if (rbtversion == NULL && trust < header->trust &&
5965 1.1 christos (ACTIVE(header, now) || header_nx)) {
5966 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
5967 1.1 christos if (addedrdataset != NULL)
5968 1.1 christos bind_rdataset(rbtdb, rbtnode, header, now,
5969 1.1 christos addedrdataset);
5970 1.1 christos return (DNS_R_UNCHANGED);
5971 1.1 christos }
5972 1.1 christos
5973 1.1 christos /*
5974 1.1 christos * Don't merge if a nonexistent rdataset is involved.
5975 1.1 christos */
5976 1.1 christos if (merge && (header_nx || newheader_nx))
5977 1.3 christos merge = false;
5978 1.1 christos
5979 1.1 christos /*
5980 1.3 christos * If 'merge' is true, we'll try to create a new rdataset
5981 1.1 christos * that is the union of 'newheader' and 'header'.
5982 1.1 christos */
5983 1.1 christos if (merge) {
5984 1.1 christos unsigned int flags = 0;
5985 1.1 christos INSIST(rbtversion->serial >= header->serial);
5986 1.1 christos merged = NULL;
5987 1.1 christos result = ISC_R_SUCCESS;
5988 1.1 christos
5989 1.1 christos if ((options & DNS_DBADD_EXACT) != 0)
5990 1.1 christos flags |= DNS_RDATASLAB_EXACT;
5991 1.1 christos /*
5992 1.1 christos * TTL use here is irrelevant to the cache;
5993 1.1 christos * merge is only done with zonedbs.
5994 1.1 christos */
5995 1.1 christos if ((options & DNS_DBADD_EXACTTTL) != 0 &&
5996 1.1 christos newheader->rdh_ttl != header->rdh_ttl)
5997 1.1 christos result = DNS_R_NOTEXACT;
5998 1.1 christos else if (newheader->rdh_ttl != header->rdh_ttl)
5999 1.1 christos flags |= DNS_RDATASLAB_FORCE;
6000 1.1 christos if (result == ISC_R_SUCCESS)
6001 1.1 christos result = dns_rdataslab_merge(
6002 1.1 christos (unsigned char *)header,
6003 1.1 christos (unsigned char *)newheader,
6004 1.1 christos (unsigned int)(sizeof(*newheader)),
6005 1.1 christos rbtdb->common.mctx,
6006 1.1 christos rbtdb->common.rdclass,
6007 1.1 christos (dns_rdatatype_t)header->type,
6008 1.1 christos flags, &merged);
6009 1.1 christos if (result == ISC_R_SUCCESS) {
6010 1.1 christos /*
6011 1.1 christos * If 'header' has the same serial number as
6012 1.1 christos * we do, we could clean it up now if we knew
6013 1.1 christos * that our caller had no references to it.
6014 1.1 christos * We don't know this, however, so we leave it
6015 1.1 christos * alone. It will get cleaned up when
6016 1.1 christos * clean_zone_node() runs.
6017 1.1 christos */
6018 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx,
6019 1.1 christos newheader);
6020 1.1 christos newheader = (rdatasetheader_t *)merged;
6021 1.1 christos init_rdataset(rbtdb, newheader);
6022 1.1 christos update_newheader(newheader, header);
6023 1.1 christos if (loading && RESIGN(newheader) &&
6024 1.1 christos RESIGN(header) &&
6025 1.1 christos resign_sooner(header, newheader))
6026 1.1 christos {
6027 1.1 christos newheader->resign = header->resign;
6028 1.1 christos newheader->resign_lsb =
6029 1.1 christos header->resign_lsb;
6030 1.1 christos }
6031 1.1 christos } else {
6032 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx,
6033 1.1 christos newheader);
6034 1.1 christos return (result);
6035 1.1 christos }
6036 1.1 christos }
6037 1.1 christos /*
6038 1.1 christos * Don't replace existing NS, A and AAAA RRsets in the
6039 1.1 christos * cache if they are already exist. This prevents named
6040 1.1 christos * being locked to old servers. Don't lower trust of
6041 1.1 christos * existing record if the update is forced. Nothing
6042 1.1 christos * special to be done w.r.t stale data; it gets replaced
6043 1.1 christos * normally further down.
6044 1.1 christos */
6045 1.1 christos if (IS_CACHE(rbtdb) && ACTIVE(header, now) &&
6046 1.1 christos header->type == dns_rdatatype_ns &&
6047 1.1 christos !header_nx && !newheader_nx &&
6048 1.1 christos header->trust >= newheader->trust &&
6049 1.1 christos dns_rdataslab_equalx((unsigned char *)header,
6050 1.1 christos (unsigned char *)newheader,
6051 1.1 christos (unsigned int)(sizeof(*newheader)),
6052 1.1 christos rbtdb->common.rdclass,
6053 1.1 christos (dns_rdatatype_t)header->type)) {
6054 1.1 christos /*
6055 1.1 christos * Honour the new ttl if it is less than the
6056 1.1 christos * older one.
6057 1.1 christos */
6058 1.1 christos if (header->rdh_ttl > newheader->rdh_ttl)
6059 1.1 christos set_ttl(rbtdb, header, newheader->rdh_ttl);
6060 1.1 christos if (header->noqname == NULL &&
6061 1.1 christos newheader->noqname != NULL) {
6062 1.1 christos header->noqname = newheader->noqname;
6063 1.1 christos newheader->noqname = NULL;
6064 1.1 christos }
6065 1.1 christos if (header->closest == NULL &&
6066 1.1 christos newheader->closest != NULL) {
6067 1.1 christos header->closest = newheader->closest;
6068 1.1 christos newheader->closest = NULL;
6069 1.1 christos }
6070 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
6071 1.1 christos if (addedrdataset != NULL)
6072 1.1 christos bind_rdataset(rbtdb, rbtnode, header, now,
6073 1.1 christos addedrdataset);
6074 1.1 christos return (ISC_R_SUCCESS);
6075 1.1 christos }
6076 1.1 christos /*
6077 1.1 christos * If we have will be replacing a NS RRset force its TTL
6078 1.1 christos * to be no more than the current NS RRset's TTL. This
6079 1.1 christos * ensures the delegations that are withdrawn are honoured.
6080 1.1 christos */
6081 1.1 christos if (IS_CACHE(rbtdb) && ACTIVE(header, now) &&
6082 1.1 christos header->type == dns_rdatatype_ns &&
6083 1.1 christos !header_nx && !newheader_nx &&
6084 1.1 christos header->trust <= newheader->trust) {
6085 1.1 christos if (newheader->rdh_ttl > header->rdh_ttl) {
6086 1.1 christos newheader->rdh_ttl = header->rdh_ttl;
6087 1.1 christos }
6088 1.1 christos }
6089 1.1 christos if (IS_CACHE(rbtdb) && ACTIVE(header, now) &&
6090 1.1 christos (options & DNS_DBADD_PREFETCH) == 0 &&
6091 1.1 christos (header->type == dns_rdatatype_a ||
6092 1.1 christos header->type == dns_rdatatype_aaaa ||
6093 1.1 christos header->type == dns_rdatatype_ds ||
6094 1.1 christos header->type == RBTDB_RDATATYPE_SIGDDS) &&
6095 1.1 christos !header_nx && !newheader_nx &&
6096 1.1 christos header->trust >= newheader->trust &&
6097 1.1 christos dns_rdataslab_equal((unsigned char *)header,
6098 1.1 christos (unsigned char *)newheader,
6099 1.1 christos (unsigned int)(sizeof(*newheader)))) {
6100 1.1 christos /*
6101 1.1 christos * Honour the new ttl if it is less than the
6102 1.1 christos * older one.
6103 1.1 christos */
6104 1.1 christos if (header->rdh_ttl > newheader->rdh_ttl)
6105 1.1 christos set_ttl(rbtdb, header, newheader->rdh_ttl);
6106 1.1 christos if (header->noqname == NULL &&
6107 1.1 christos newheader->noqname != NULL) {
6108 1.1 christos header->noqname = newheader->noqname;
6109 1.1 christos newheader->noqname = NULL;
6110 1.1 christos }
6111 1.1 christos if (header->closest == NULL &&
6112 1.1 christos newheader->closest != NULL) {
6113 1.1 christos header->closest = newheader->closest;
6114 1.1 christos newheader->closest = NULL;
6115 1.1 christos }
6116 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
6117 1.1 christos if (addedrdataset != NULL)
6118 1.1 christos bind_rdataset(rbtdb, rbtnode, header, now,
6119 1.1 christos addedrdataset);
6120 1.1 christos return (ISC_R_SUCCESS);
6121 1.1 christos }
6122 1.1 christos INSIST(rbtversion == NULL ||
6123 1.1 christos rbtversion->serial >= topheader->serial);
6124 1.1 christos if (loading) {
6125 1.1 christos newheader->down = NULL;
6126 1.1 christos idx = newheader->node->locknum;
6127 1.1 christos if (IS_CACHE(rbtdb)) {
6128 1.1 christos if (ZEROTTL(newheader))
6129 1.1 christos ISC_LIST_APPEND(rbtdb->rdatasets[idx],
6130 1.1 christos newheader, link);
6131 1.1 christos else
6132 1.1 christos ISC_LIST_PREPEND(rbtdb->rdatasets[idx],
6133 1.1 christos newheader, link);
6134 1.1 christos INSIST(rbtdb->heaps != NULL);
6135 1.1 christos result = isc_heap_insert(rbtdb->heaps[idx],
6136 1.1 christos newheader);
6137 1.1 christos if (result != ISC_R_SUCCESS) {
6138 1.1 christos free_rdataset(rbtdb,
6139 1.1 christos rbtdb->common.mctx,
6140 1.1 christos newheader);
6141 1.1 christos return (result);
6142 1.1 christos }
6143 1.1 christos } else if (RESIGN(newheader)) {
6144 1.1 christos result = resign_insert(rbtdb, idx, newheader);
6145 1.1 christos if (result != ISC_R_SUCCESS) {
6146 1.1 christos free_rdataset(rbtdb,
6147 1.1 christos rbtdb->common.mctx,
6148 1.1 christos newheader);
6149 1.1 christos return (result);
6150 1.1 christos }
6151 1.1 christos /*
6152 1.1 christos * Don't call resign_delete as we don't need
6153 1.1 christos * to reverse the delete. The free_rdataset
6154 1.1 christos * call below will clean up the heap entry.
6155 1.1 christos */
6156 1.1 christos }
6157 1.1 christos
6158 1.1 christos /*
6159 1.1 christos * There are no other references to 'header' when
6160 1.1 christos * loading, so we MAY clean up 'header' now.
6161 1.1 christos * Since we don't generate changed records when
6162 1.1 christos * loading, we MUST clean up 'header' now.
6163 1.1 christos */
6164 1.1 christos if (topheader_prev != NULL)
6165 1.1 christos topheader_prev->next = newheader;
6166 1.1 christos else
6167 1.1 christos rbtnode->data = newheader;
6168 1.1 christos newheader->next = topheader->next;
6169 1.1 christos if (rbtversion != NULL && !header_nx) {
6170 1.1 christos RWLOCK(&rbtversion->rwlock,
6171 1.1 christos isc_rwlocktype_write);
6172 1.3 christos update_recordsandbytes(false, rbtversion,
6173 1.1 christos header);
6174 1.1 christos RWUNLOCK(&rbtversion->rwlock,
6175 1.1 christos isc_rwlocktype_write);
6176 1.1 christos }
6177 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx, header);
6178 1.1 christos } else {
6179 1.1 christos idx = newheader->node->locknum;
6180 1.1 christos if (IS_CACHE(rbtdb)) {
6181 1.1 christos INSIST(rbtdb->heaps != NULL);
6182 1.1 christos result = isc_heap_insert(rbtdb->heaps[idx],
6183 1.1 christos newheader);
6184 1.1 christos if (result != ISC_R_SUCCESS) {
6185 1.1 christos free_rdataset(rbtdb,
6186 1.1 christos rbtdb->common.mctx,
6187 1.1 christos newheader);
6188 1.1 christos return (result);
6189 1.1 christos }
6190 1.1 christos if (ZEROTTL(newheader))
6191 1.1 christos ISC_LIST_APPEND(rbtdb->rdatasets[idx],
6192 1.1 christos newheader, link);
6193 1.1 christos else
6194 1.1 christos ISC_LIST_PREPEND(rbtdb->rdatasets[idx],
6195 1.1 christos newheader, link);
6196 1.1 christos } else if (RESIGN(newheader)) {
6197 1.1 christos result = resign_insert(rbtdb, idx, newheader);
6198 1.1 christos if (result != ISC_R_SUCCESS) {
6199 1.1 christos free_rdataset(rbtdb,
6200 1.1 christos rbtdb->common.mctx,
6201 1.1 christos newheader);
6202 1.1 christos return (result);
6203 1.1 christos }
6204 1.1 christos resign_delete(rbtdb, rbtversion, header);
6205 1.1 christos }
6206 1.1 christos if (topheader_prev != NULL)
6207 1.1 christos topheader_prev->next = newheader;
6208 1.1 christos else
6209 1.1 christos rbtnode->data = newheader;
6210 1.1 christos newheader->next = topheader->next;
6211 1.1 christos newheader->down = topheader;
6212 1.1 christos topheader->next = newheader;
6213 1.1 christos rbtnode->dirty = 1;
6214 1.1 christos if (changed != NULL)
6215 1.3 christos changed->dirty = true;
6216 1.1 christos if (rbtversion == NULL) {
6217 1.1 christos set_ttl(rbtdb, header, 0);
6218 1.1 christos mark_header_ancient(rbtdb, header);
6219 1.1 christos if (sigheader != NULL) {
6220 1.1 christos set_ttl(rbtdb, sigheader, 0);
6221 1.1 christos mark_header_ancient(rbtdb, sigheader);
6222 1.1 christos }
6223 1.1 christos }
6224 1.1 christos if (rbtversion != NULL && !header_nx) {
6225 1.1 christos RWLOCK(&rbtversion->rwlock,
6226 1.1 christos isc_rwlocktype_write);
6227 1.3 christos update_recordsandbytes(false, rbtversion,
6228 1.1 christos header);
6229 1.1 christos RWUNLOCK(&rbtversion->rwlock,
6230 1.1 christos isc_rwlocktype_write);
6231 1.1 christos }
6232 1.1 christos }
6233 1.1 christos } else {
6234 1.1 christos /*
6235 1.1 christos * No non-IGNORED rdatasets of the given type exist at
6236 1.1 christos * this node.
6237 1.1 christos */
6238 1.1 christos
6239 1.1 christos /*
6240 1.1 christos * If we're trying to delete the type, don't bother.
6241 1.1 christos */
6242 1.1 christos if (newheader_nx) {
6243 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
6244 1.1 christos return (DNS_R_UNCHANGED);
6245 1.1 christos }
6246 1.1 christos
6247 1.1 christos idx = newheader->node->locknum;
6248 1.1 christos if (IS_CACHE(rbtdb)) {
6249 1.1 christos result = isc_heap_insert(rbtdb->heaps[idx], newheader);
6250 1.1 christos if (result != ISC_R_SUCCESS) {
6251 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx,
6252 1.1 christos newheader);
6253 1.1 christos return (result);
6254 1.1 christos }
6255 1.1 christos if (ZEROTTL(newheader))
6256 1.1 christos ISC_LIST_APPEND(rbtdb->rdatasets[idx],
6257 1.1 christos newheader, link);
6258 1.1 christos else
6259 1.1 christos ISC_LIST_PREPEND(rbtdb->rdatasets[idx],
6260 1.1 christos newheader, link);
6261 1.1 christos } else if (RESIGN(newheader)) {
6262 1.1 christos result = resign_insert(rbtdb, idx, newheader);
6263 1.1 christos if (result != ISC_R_SUCCESS) {
6264 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx,
6265 1.1 christos newheader);
6266 1.1 christos return (result);
6267 1.1 christos }
6268 1.1 christos resign_delete(rbtdb, rbtversion, header);
6269 1.1 christos }
6270 1.1 christos
6271 1.1 christos if (topheader != NULL) {
6272 1.1 christos /*
6273 1.1 christos * We have an list of rdatasets of the given type,
6274 1.1 christos * but they're all marked IGNORE. We simply insert
6275 1.1 christos * the new rdataset at the head of the list.
6276 1.1 christos *
6277 1.1 christos * Ignored rdatasets cannot occur during loading, so
6278 1.1 christos * we INSIST on it.
6279 1.1 christos */
6280 1.1 christos INSIST(!loading);
6281 1.1 christos INSIST(rbtversion == NULL ||
6282 1.1 christos rbtversion->serial >= topheader->serial);
6283 1.1 christos if (topheader_prev != NULL)
6284 1.1 christos topheader_prev->next = newheader;
6285 1.1 christos else
6286 1.1 christos rbtnode->data = newheader;
6287 1.1 christos newheader->next = topheader->next;
6288 1.1 christos newheader->down = topheader;
6289 1.1 christos topheader->next = newheader;
6290 1.1 christos rbtnode->dirty = 1;
6291 1.1 christos if (changed != NULL)
6292 1.3 christos changed->dirty = true;
6293 1.1 christos } else {
6294 1.1 christos /*
6295 1.1 christos * No rdatasets of the given type exist at the node.
6296 1.1 christos */
6297 1.1 christos newheader->next = rbtnode->data;
6298 1.1 christos newheader->down = NULL;
6299 1.1 christos rbtnode->data = newheader;
6300 1.1 christos }
6301 1.1 christos }
6302 1.1 christos
6303 1.1 christos if (rbtversion != NULL && !newheader_nx) {
6304 1.1 christos RWLOCK(&rbtversion->rwlock, isc_rwlocktype_write);
6305 1.3 christos update_recordsandbytes(true, rbtversion, newheader);
6306 1.1 christos RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_write);
6307 1.1 christos }
6308 1.1 christos
6309 1.1 christos /*
6310 1.1 christos * Check if the node now contains CNAME and other data.
6311 1.1 christos */
6312 1.1 christos if (rbtversion != NULL &&
6313 1.1 christos cname_and_other_data(rbtnode, rbtversion->serial))
6314 1.1 christos return (DNS_R_CNAMEANDOTHER);
6315 1.1 christos
6316 1.1 christos if (addedrdataset != NULL)
6317 1.1 christos bind_rdataset(rbtdb, rbtnode, newheader, now, addedrdataset);
6318 1.1 christos
6319 1.1 christos return (ISC_R_SUCCESS);
6320 1.1 christos }
6321 1.1 christos
6322 1.3 christos static inline bool
6323 1.1 christos delegating_type(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
6324 1.1 christos rbtdb_rdatatype_t type)
6325 1.1 christos {
6326 1.1 christos if (IS_CACHE(rbtdb)) {
6327 1.1 christos if (type == dns_rdatatype_dname)
6328 1.3 christos return (true);
6329 1.1 christos else
6330 1.3 christos return (false);
6331 1.1 christos } else if (type == dns_rdatatype_dname ||
6332 1.1 christos (type == dns_rdatatype_ns &&
6333 1.1 christos (node != rbtdb->origin_node || IS_STUB(rbtdb))))
6334 1.3 christos return (true);
6335 1.3 christos return (false);
6336 1.1 christos }
6337 1.1 christos
6338 1.1 christos static inline isc_result_t
6339 1.1 christos addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader,
6340 1.1 christos dns_rdataset_t *rdataset)
6341 1.1 christos {
6342 1.1 christos struct noqname *noqname;
6343 1.1 christos isc_mem_t *mctx = rbtdb->common.mctx;
6344 1.1 christos dns_name_t name;
6345 1.1 christos dns_rdataset_t neg, negsig;
6346 1.1 christos isc_result_t result;
6347 1.1 christos isc_region_t r;
6348 1.1 christos
6349 1.1 christos dns_name_init(&name, NULL);
6350 1.1 christos dns_rdataset_init(&neg);
6351 1.1 christos dns_rdataset_init(&negsig);
6352 1.1 christos
6353 1.1 christos result = dns_rdataset_getnoqname(rdataset, &name, &neg, &negsig);
6354 1.1 christos RUNTIME_CHECK(result == ISC_R_SUCCESS);
6355 1.1 christos
6356 1.1 christos noqname = isc_mem_get(mctx, sizeof(*noqname));
6357 1.1 christos if (noqname == NULL) {
6358 1.1 christos result = ISC_R_NOMEMORY;
6359 1.1 christos goto cleanup;
6360 1.1 christos }
6361 1.1 christos dns_name_init(&noqname->name, NULL);
6362 1.1 christos noqname->neg = NULL;
6363 1.1 christos noqname->negsig = NULL;
6364 1.1 christos noqname->type = neg.type;
6365 1.1 christos result = dns_name_dup(&name, mctx, &noqname->name);
6366 1.1 christos if (result != ISC_R_SUCCESS)
6367 1.1 christos goto cleanup;
6368 1.1 christos result = dns_rdataslab_fromrdataset(&neg, mctx, &r, 0);
6369 1.1 christos if (result != ISC_R_SUCCESS)
6370 1.1 christos goto cleanup;
6371 1.1 christos noqname->neg = r.base;
6372 1.1 christos result = dns_rdataslab_fromrdataset(&negsig, mctx, &r, 0);
6373 1.1 christos if (result != ISC_R_SUCCESS)
6374 1.1 christos goto cleanup;
6375 1.1 christos noqname->negsig = r.base;
6376 1.1 christos dns_rdataset_disassociate(&neg);
6377 1.1 christos dns_rdataset_disassociate(&negsig);
6378 1.1 christos newheader->noqname = noqname;
6379 1.1 christos return (ISC_R_SUCCESS);
6380 1.1 christos
6381 1.1 christos cleanup:
6382 1.1 christos dns_rdataset_disassociate(&neg);
6383 1.1 christos dns_rdataset_disassociate(&negsig);
6384 1.1 christos if (noqname != NULL)
6385 1.1 christos free_noqname(mctx, &noqname);
6386 1.1 christos return(result);
6387 1.1 christos }
6388 1.1 christos
6389 1.1 christos static inline isc_result_t
6390 1.1 christos addclosest(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader,
6391 1.1 christos dns_rdataset_t *rdataset)
6392 1.1 christos {
6393 1.1 christos struct noqname *closest;
6394 1.1 christos isc_mem_t *mctx = rbtdb->common.mctx;
6395 1.1 christos dns_name_t name;
6396 1.1 christos dns_rdataset_t neg, negsig;
6397 1.1 christos isc_result_t result;
6398 1.1 christos isc_region_t r;
6399 1.1 christos
6400 1.1 christos dns_name_init(&name, NULL);
6401 1.1 christos dns_rdataset_init(&neg);
6402 1.1 christos dns_rdataset_init(&negsig);
6403 1.1 christos
6404 1.1 christos result = dns_rdataset_getclosest(rdataset, &name, &neg, &negsig);
6405 1.1 christos RUNTIME_CHECK(result == ISC_R_SUCCESS);
6406 1.1 christos
6407 1.1 christos closest = isc_mem_get(mctx, sizeof(*closest));
6408 1.1 christos if (closest == NULL) {
6409 1.1 christos result = ISC_R_NOMEMORY;
6410 1.1 christos goto cleanup;
6411 1.1 christos }
6412 1.1 christos dns_name_init(&closest->name, NULL);
6413 1.1 christos closest->neg = NULL;
6414 1.1 christos closest->negsig = NULL;
6415 1.1 christos closest->type = neg.type;
6416 1.1 christos result = dns_name_dup(&name, mctx, &closest->name);
6417 1.1 christos if (result != ISC_R_SUCCESS)
6418 1.1 christos goto cleanup;
6419 1.1 christos result = dns_rdataslab_fromrdataset(&neg, mctx, &r, 0);
6420 1.1 christos if (result != ISC_R_SUCCESS)
6421 1.1 christos goto cleanup;
6422 1.1 christos closest->neg = r.base;
6423 1.1 christos result = dns_rdataslab_fromrdataset(&negsig, mctx, &r, 0);
6424 1.1 christos if (result != ISC_R_SUCCESS)
6425 1.1 christos goto cleanup;
6426 1.1 christos closest->negsig = r.base;
6427 1.1 christos dns_rdataset_disassociate(&neg);
6428 1.1 christos dns_rdataset_disassociate(&negsig);
6429 1.1 christos newheader->closest = closest;
6430 1.1 christos return (ISC_R_SUCCESS);
6431 1.1 christos
6432 1.1 christos cleanup:
6433 1.1 christos dns_rdataset_disassociate(&neg);
6434 1.1 christos dns_rdataset_disassociate(&negsig);
6435 1.1 christos if (closest != NULL)
6436 1.1 christos free_noqname(mctx, &closest);
6437 1.1 christos return(result);
6438 1.1 christos }
6439 1.1 christos
6440 1.1 christos static dns_dbmethods_t zone_methods;
6441 1.1 christos
6442 1.1 christos static isc_result_t
6443 1.1 christos addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
6444 1.1 christos isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options,
6445 1.1 christos dns_rdataset_t *addedrdataset)
6446 1.1 christos {
6447 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
6448 1.1 christos dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
6449 1.1 christos rbtdb_version_t *rbtversion = version;
6450 1.1 christos isc_region_t region;
6451 1.1 christos rdatasetheader_t *newheader;
6452 1.1 christos rdatasetheader_t *header;
6453 1.1 christos isc_result_t result;
6454 1.3 christos bool delegating;
6455 1.3 christos bool newnsec;
6456 1.3 christos bool tree_locked = false;
6457 1.3 christos bool cache_is_overmem = false;
6458 1.1 christos dns_fixedname_t fixed;
6459 1.1 christos dns_name_t *name;
6460 1.1 christos
6461 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
6462 1.1 christos INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb);
6463 1.1 christos
6464 1.1 christos if (rbtdb->common.methods == &zone_methods)
6465 1.1 christos REQUIRE(((rbtnode->nsec == DNS_RBT_NSEC_NSEC3 &&
6466 1.1 christos (rdataset->type == dns_rdatatype_nsec3 ||
6467 1.1 christos rdataset->covers == dns_rdatatype_nsec3)) ||
6468 1.1 christos (rbtnode->nsec != DNS_RBT_NSEC_NSEC3 &&
6469 1.1 christos rdataset->type != dns_rdatatype_nsec3 &&
6470 1.1 christos rdataset->covers != dns_rdatatype_nsec3)));
6471 1.1 christos
6472 1.1 christos if (rbtversion == NULL) {
6473 1.1 christos if (now == 0)
6474 1.1 christos isc_stdtime_get(&now);
6475 1.1 christos } else
6476 1.1 christos now = 0;
6477 1.1 christos
6478 1.1 christos result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx,
6479 1.1 christos ®ion, sizeof(rdatasetheader_t));
6480 1.1 christos if (result != ISC_R_SUCCESS)
6481 1.1 christos return (result);
6482 1.1 christos
6483 1.1 christos name = dns_fixedname_initname(&fixed);
6484 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
6485 1.1 christos dns_rbt_fullnamefromnode(node, name);
6486 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
6487 1.1 christos dns_rdataset_getownercase(rdataset, name);
6488 1.1 christos
6489 1.1 christos newheader = (rdatasetheader_t *)region.base;
6490 1.1 christos init_rdataset(rbtdb, newheader);
6491 1.1 christos setownercase(newheader, name);
6492 1.1 christos set_ttl(rbtdb, newheader, rdataset->ttl + now);
6493 1.1 christos newheader->type = RBTDB_RDATATYPE_VALUE(rdataset->type,
6494 1.1 christos rdataset->covers);
6495 1.1 christos newheader->attributes = 0;
6496 1.1 christos if (rdataset->ttl == 0U)
6497 1.1 christos newheader->attributes |= RDATASET_ATTR_ZEROTTL;
6498 1.1 christos newheader->noqname = NULL;
6499 1.1 christos newheader->closest = NULL;
6500 1.1 christos newheader->count = init_count++;
6501 1.1 christos newheader->trust = rdataset->trust;
6502 1.1 christos newheader->last_used = now;
6503 1.1 christos newheader->node = rbtnode;
6504 1.1 christos if (rbtversion != NULL) {
6505 1.1 christos newheader->serial = rbtversion->serial;
6506 1.1 christos now = 0;
6507 1.1 christos
6508 1.1 christos if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) {
6509 1.1 christos newheader->attributes |= RDATASET_ATTR_RESIGN;
6510 1.1 christos newheader->resign = (isc_stdtime_t)
6511 1.1 christos (dns_time64_from32(rdataset->resign) >> 1);
6512 1.1 christos newheader->resign_lsb = rdataset->resign & 0x1;
6513 1.1 christos } else {
6514 1.1 christos newheader->resign = 0;
6515 1.1 christos newheader->resign_lsb = 0;
6516 1.1 christos }
6517 1.1 christos } else {
6518 1.1 christos newheader->serial = 1;
6519 1.1 christos newheader->resign = 0;
6520 1.1 christos newheader->resign_lsb = 0;
6521 1.1 christos if ((rdataset->attributes & DNS_RDATASETATTR_PREFETCH) != 0)
6522 1.1 christos newheader->attributes |= RDATASET_ATTR_PREFETCH;
6523 1.1 christos if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0)
6524 1.1 christos newheader->attributes |= RDATASET_ATTR_NEGATIVE;
6525 1.1 christos if ((rdataset->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
6526 1.1 christos newheader->attributes |= RDATASET_ATTR_NXDOMAIN;
6527 1.1 christos if ((rdataset->attributes & DNS_RDATASETATTR_OPTOUT) != 0)
6528 1.1 christos newheader->attributes |= RDATASET_ATTR_OPTOUT;
6529 1.1 christos if ((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0) {
6530 1.1 christos result = addnoqname(rbtdb, newheader, rdataset);
6531 1.1 christos if (result != ISC_R_SUCCESS) {
6532 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx,
6533 1.1 christos newheader);
6534 1.1 christos return (result);
6535 1.1 christos }
6536 1.1 christos }
6537 1.1 christos if ((rdataset->attributes & DNS_RDATASETATTR_CLOSEST) != 0) {
6538 1.1 christos result = addclosest(rbtdb, newheader, rdataset);
6539 1.1 christos if (result != ISC_R_SUCCESS) {
6540 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx,
6541 1.1 christos newheader);
6542 1.1 christos return (result);
6543 1.1 christos }
6544 1.1 christos }
6545 1.1 christos }
6546 1.1 christos
6547 1.1 christos /*
6548 1.1 christos * If we're adding a delegation type (e.g. NS or DNAME for a zone,
6549 1.1 christos * just DNAME for the cache), then we need to set the callback bit
6550 1.1 christos * on the node.
6551 1.1 christos */
6552 1.1 christos if (delegating_type(rbtdb, rbtnode, rdataset->type))
6553 1.3 christos delegating = true;
6554 1.1 christos else
6555 1.3 christos delegating = false;
6556 1.1 christos
6557 1.1 christos /*
6558 1.1 christos * Add to the auxiliary NSEC tree if we're adding an NSEC record.
6559 1.1 christos */
6560 1.1 christos if (rbtnode->nsec != DNS_RBT_NSEC_HAS_NSEC &&
6561 1.1 christos rdataset->type == dns_rdatatype_nsec)
6562 1.3 christos newnsec = true;
6563 1.1 christos else
6564 1.3 christos newnsec = false;
6565 1.1 christos
6566 1.1 christos /*
6567 1.1 christos * If we're adding a delegation type, adding to the auxiliary NSEC tree,
6568 1.1 christos * or the DB is a cache in an overmem state, hold an exclusive lock on
6569 1.1 christos * the tree. In the latter case the lock does not necessarily have to
6570 1.1 christos * be acquired but it will help purge stale entries more effectively.
6571 1.1 christos */
6572 1.1 christos if (IS_CACHE(rbtdb) && isc_mem_isovermem(rbtdb->common.mctx))
6573 1.3 christos cache_is_overmem = true;
6574 1.1 christos if (delegating || newnsec || cache_is_overmem) {
6575 1.3 christos tree_locked = true;
6576 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
6577 1.1 christos }
6578 1.1 christos
6579 1.1 christos if (cache_is_overmem)
6580 1.1 christos overmem_purge(rbtdb, rbtnode->locknum, now, tree_locked);
6581 1.1 christos
6582 1.1 christos NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
6583 1.1 christos isc_rwlocktype_write);
6584 1.1 christos
6585 1.1 christos if (rbtdb->rrsetstats != NULL) {
6586 1.1 christos newheader->attributes |= RDATASET_ATTR_STATCOUNT;
6587 1.3 christos update_rrsetstats(rbtdb, newheader, true);
6588 1.1 christos }
6589 1.1 christos
6590 1.1 christos if (IS_CACHE(rbtdb)) {
6591 1.1 christos if (tree_locked)
6592 1.1 christos cleanup_dead_nodes(rbtdb, rbtnode->locknum);
6593 1.1 christos
6594 1.1 christos header = isc_heap_element(rbtdb->heaps[rbtnode->locknum], 1);
6595 1.1 christos if (header && header->rdh_ttl < now - RBTDB_VIRTUAL)
6596 1.1 christos expire_header(rbtdb, header, tree_locked,
6597 1.1 christos expire_ttl);
6598 1.1 christos
6599 1.1 christos /*
6600 1.1 christos * If we've been holding a write lock on the tree just for
6601 1.1 christos * cleaning, we can release it now. However, we still need the
6602 1.1 christos * node lock.
6603 1.1 christos */
6604 1.1 christos if (tree_locked && !delegating && !newnsec) {
6605 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
6606 1.3 christos tree_locked = false;
6607 1.1 christos }
6608 1.1 christos }
6609 1.1 christos
6610 1.1 christos result = ISC_R_SUCCESS;
6611 1.1 christos if (newnsec) {
6612 1.1 christos dns_rbtnode_t *nsecnode;
6613 1.1 christos
6614 1.1 christos dns_rbt_fullnamefromnode(rbtnode, name);
6615 1.1 christos nsecnode = NULL;
6616 1.1 christos result = dns_rbt_addnode(rbtdb->nsec, name, &nsecnode);
6617 1.1 christos if (result == ISC_R_SUCCESS) {
6618 1.1 christos nsecnode->nsec = DNS_RBT_NSEC_NSEC;
6619 1.1 christos rbtnode->nsec = DNS_RBT_NSEC_HAS_NSEC;
6620 1.1 christos } else if (result == ISC_R_EXISTS) {
6621 1.1 christos rbtnode->nsec = DNS_RBT_NSEC_HAS_NSEC;
6622 1.1 christos result = ISC_R_SUCCESS;
6623 1.1 christos }
6624 1.1 christos }
6625 1.1 christos
6626 1.1 christos if (result == ISC_R_SUCCESS)
6627 1.1 christos result = add32(rbtdb, rbtnode, rbtversion, newheader, options,
6628 1.3 christos false, addedrdataset, now);
6629 1.1 christos if (result == ISC_R_SUCCESS && delegating)
6630 1.1 christos rbtnode->find_callback = 1;
6631 1.1 christos
6632 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
6633 1.1 christos isc_rwlocktype_write);
6634 1.1 christos
6635 1.1 christos if (tree_locked)
6636 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
6637 1.1 christos
6638 1.1 christos /*
6639 1.1 christos * Update the zone's secure status. If version is non-NULL
6640 1.1 christos * this is deferred until closeversion() is called.
6641 1.1 christos */
6642 1.1 christos if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb))
6643 1.1 christos iszonesecure(db, version, rbtdb->origin_node);
6644 1.1 christos
6645 1.1 christos return (result);
6646 1.1 christos }
6647 1.1 christos
6648 1.1 christos static isc_result_t
6649 1.1 christos subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
6650 1.1 christos dns_rdataset_t *rdataset, unsigned int options,
6651 1.1 christos dns_rdataset_t *newrdataset)
6652 1.1 christos {
6653 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
6654 1.1 christos dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
6655 1.1 christos rbtdb_version_t *rbtversion = version;
6656 1.1 christos rdatasetheader_t *topheader, *topheader_prev, *header, *newheader;
6657 1.1 christos unsigned char *subresult;
6658 1.1 christos isc_region_t region;
6659 1.1 christos isc_result_t result;
6660 1.1 christos rbtdb_changed_t *changed;
6661 1.1 christos
6662 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
6663 1.1 christos REQUIRE(rbtversion != NULL && rbtversion->rbtdb == rbtdb);
6664 1.1 christos
6665 1.1 christos if (rbtdb->common.methods == &zone_methods)
6666 1.1 christos REQUIRE(((rbtnode->nsec == DNS_RBT_NSEC_NSEC3 &&
6667 1.1 christos (rdataset->type == dns_rdatatype_nsec3 ||
6668 1.1 christos rdataset->covers == dns_rdatatype_nsec3)) ||
6669 1.1 christos (rbtnode->nsec != DNS_RBT_NSEC_NSEC3 &&
6670 1.1 christos rdataset->type != dns_rdatatype_nsec3 &&
6671 1.1 christos rdataset->covers != dns_rdatatype_nsec3)));
6672 1.1 christos
6673 1.1 christos result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx,
6674 1.1 christos ®ion, sizeof(rdatasetheader_t));
6675 1.1 christos if (result != ISC_R_SUCCESS)
6676 1.1 christos return (result);
6677 1.1 christos newheader = (rdatasetheader_t *)region.base;
6678 1.1 christos init_rdataset(rbtdb, newheader);
6679 1.1 christos set_ttl(rbtdb, newheader, rdataset->ttl);
6680 1.1 christos newheader->type = RBTDB_RDATATYPE_VALUE(rdataset->type,
6681 1.1 christos rdataset->covers);
6682 1.1 christos newheader->attributes = 0;
6683 1.1 christos newheader->serial = rbtversion->serial;
6684 1.1 christos newheader->trust = 0;
6685 1.1 christos newheader->noqname = NULL;
6686 1.1 christos newheader->closest = NULL;
6687 1.1 christos newheader->count = init_count++;
6688 1.1 christos newheader->last_used = 0;
6689 1.1 christos newheader->node = rbtnode;
6690 1.1 christos if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) {
6691 1.1 christos newheader->attributes |= RDATASET_ATTR_RESIGN;
6692 1.1 christos newheader->resign = (isc_stdtime_t)
6693 1.1 christos (dns_time64_from32(rdataset->resign) >> 1);
6694 1.1 christos newheader->resign_lsb = rdataset->resign & 0x1;
6695 1.1 christos } else {
6696 1.1 christos newheader->resign = 0;
6697 1.1 christos newheader->resign_lsb = 0;
6698 1.1 christos }
6699 1.1 christos
6700 1.1 christos NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
6701 1.1 christos isc_rwlocktype_write);
6702 1.1 christos
6703 1.1 christos changed = add_changed(rbtdb, rbtversion, rbtnode);
6704 1.1 christos if (changed == NULL) {
6705 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
6706 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
6707 1.1 christos isc_rwlocktype_write);
6708 1.1 christos return (ISC_R_NOMEMORY);
6709 1.1 christos }
6710 1.1 christos
6711 1.1 christos topheader_prev = NULL;
6712 1.1 christos for (topheader = rbtnode->data;
6713 1.1 christos topheader != NULL;
6714 1.1 christos topheader = topheader->next) {
6715 1.1 christos if (topheader->type == newheader->type)
6716 1.1 christos break;
6717 1.1 christos topheader_prev = topheader;
6718 1.1 christos }
6719 1.1 christos /*
6720 1.1 christos * If header isn't NULL, we've found the right type. There may be
6721 1.1 christos * IGNORE rdatasets between the top of the chain and the first real
6722 1.1 christos * data. We skip over them.
6723 1.1 christos */
6724 1.1 christos header = topheader;
6725 1.1 christos while (header != NULL && IGNORE(header))
6726 1.1 christos header = header->down;
6727 1.1 christos if (header != NULL && EXISTS(header)) {
6728 1.1 christos unsigned int flags = 0;
6729 1.1 christos subresult = NULL;
6730 1.1 christos result = ISC_R_SUCCESS;
6731 1.1 christos if ((options & DNS_DBSUB_EXACT) != 0) {
6732 1.1 christos flags |= DNS_RDATASLAB_EXACT;
6733 1.1 christos if (newheader->rdh_ttl != header->rdh_ttl)
6734 1.1 christos result = DNS_R_NOTEXACT;
6735 1.1 christos }
6736 1.1 christos if (result == ISC_R_SUCCESS)
6737 1.1 christos result = dns_rdataslab_subtract(
6738 1.1 christos (unsigned char *)header,
6739 1.1 christos (unsigned char *)newheader,
6740 1.1 christos (unsigned int)(sizeof(*newheader)),
6741 1.1 christos rbtdb->common.mctx,
6742 1.1 christos rbtdb->common.rdclass,
6743 1.1 christos (dns_rdatatype_t)header->type,
6744 1.1 christos flags, &subresult);
6745 1.1 christos if (result == ISC_R_SUCCESS) {
6746 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
6747 1.1 christos newheader = (rdatasetheader_t *)subresult;
6748 1.1 christos init_rdataset(rbtdb, newheader);
6749 1.1 christos update_newheader(newheader, header);
6750 1.1 christos if (RESIGN(header)) {
6751 1.1 christos newheader->attributes |= RDATASET_ATTR_RESIGN;
6752 1.1 christos newheader->resign = header->resign;
6753 1.1 christos newheader->resign_lsb = header->resign_lsb;
6754 1.1 christos result = resign_insert(rbtdb, rbtnode->locknum,
6755 1.1 christos newheader);
6756 1.1 christos if (result != ISC_R_SUCCESS) {
6757 1.1 christos free_rdataset(rbtdb,
6758 1.1 christos rbtdb->common.mctx,
6759 1.1 christos newheader);
6760 1.1 christos goto unlock;
6761 1.1 christos }
6762 1.1 christos }
6763 1.1 christos /*
6764 1.1 christos * We have to set the serial since the rdataslab
6765 1.1 christos * subtraction routine copies the reserved portion of
6766 1.1 christos * header, not newheader.
6767 1.1 christos */
6768 1.1 christos newheader->serial = rbtversion->serial;
6769 1.1 christos /*
6770 1.1 christos * XXXJT: dns_rdataslab_subtract() copied the pointers
6771 1.1 christos * to additional info. We need to clear these fields
6772 1.1 christos * to avoid having duplicated references.
6773 1.1 christos */
6774 1.3 christos update_recordsandbytes(true, rbtversion, newheader);
6775 1.1 christos } else if (result == DNS_R_NXRRSET) {
6776 1.1 christos /*
6777 1.1 christos * This subtraction would remove all of the rdata;
6778 1.1 christos * add a nonexistent header instead.
6779 1.1 christos */
6780 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
6781 1.1 christos newheader = new_rdataset(rbtdb, rbtdb->common.mctx);
6782 1.1 christos if (newheader == NULL) {
6783 1.1 christos result = ISC_R_NOMEMORY;
6784 1.1 christos goto unlock;
6785 1.1 christos }
6786 1.1 christos init_rdataset(rbtdb, newheader);
6787 1.1 christos set_ttl(rbtdb, newheader, 0);
6788 1.1 christos newheader->type = topheader->type;
6789 1.1 christos newheader->attributes = RDATASET_ATTR_NONEXISTENT;
6790 1.1 christos newheader->trust = 0;
6791 1.1 christos newheader->serial = rbtversion->serial;
6792 1.1 christos newheader->noqname = NULL;
6793 1.1 christos newheader->closest = NULL;
6794 1.1 christos newheader->count = 0;
6795 1.1 christos newheader->node = rbtnode;
6796 1.1 christos newheader->resign = 0;
6797 1.1 christos newheader->resign_lsb = 0;
6798 1.1 christos newheader->last_used = 0;
6799 1.1 christos } else {
6800 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
6801 1.1 christos goto unlock;
6802 1.1 christos }
6803 1.1 christos
6804 1.1 christos /*
6805 1.1 christos * If we're here, we want to link newheader in front of
6806 1.1 christos * topheader.
6807 1.1 christos */
6808 1.1 christos INSIST(rbtversion->serial >= topheader->serial);
6809 1.3 christos update_recordsandbytes(false, rbtversion, header);
6810 1.1 christos if (topheader_prev != NULL)
6811 1.1 christos topheader_prev->next = newheader;
6812 1.1 christos else
6813 1.1 christos rbtnode->data = newheader;
6814 1.1 christos newheader->next = topheader->next;
6815 1.1 christos newheader->down = topheader;
6816 1.1 christos topheader->next = newheader;
6817 1.1 christos rbtnode->dirty = 1;
6818 1.3 christos changed->dirty = true;
6819 1.1 christos resign_delete(rbtdb, rbtversion, header);
6820 1.1 christos } else {
6821 1.1 christos /*
6822 1.1 christos * The rdataset doesn't exist, so we don't need to do anything
6823 1.1 christos * to satisfy the deletion request.
6824 1.1 christos */
6825 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
6826 1.1 christos if ((options & DNS_DBSUB_EXACT) != 0)
6827 1.1 christos result = DNS_R_NOTEXACT;
6828 1.1 christos else
6829 1.1 christos result = DNS_R_UNCHANGED;
6830 1.1 christos }
6831 1.1 christos
6832 1.1 christos if (result == ISC_R_SUCCESS && newrdataset != NULL)
6833 1.1 christos bind_rdataset(rbtdb, rbtnode, newheader, 0, newrdataset);
6834 1.1 christos
6835 1.1 christos if (result == DNS_R_NXRRSET && newrdataset != NULL &&
6836 1.1 christos (options & DNS_DBSUB_WANTOLD) != 0)
6837 1.1 christos bind_rdataset(rbtdb, rbtnode, header, 0, newrdataset);
6838 1.1 christos
6839 1.1 christos unlock:
6840 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
6841 1.1 christos isc_rwlocktype_write);
6842 1.1 christos
6843 1.1 christos /*
6844 1.1 christos * Update the zone's secure status. If version is non-NULL
6845 1.1 christos * this is deferred until closeversion() is called.
6846 1.1 christos */
6847 1.1 christos if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb))
6848 1.1 christos iszonesecure(db, rbtdb->current_version, rbtdb->origin_node);
6849 1.1 christos
6850 1.1 christos return (result);
6851 1.1 christos }
6852 1.1 christos
6853 1.1 christos static isc_result_t
6854 1.1 christos deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
6855 1.1 christos dns_rdatatype_t type, dns_rdatatype_t covers)
6856 1.1 christos {
6857 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
6858 1.1 christos dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
6859 1.1 christos rbtdb_version_t *rbtversion = version;
6860 1.1 christos isc_result_t result;
6861 1.1 christos rdatasetheader_t *newheader;
6862 1.1 christos
6863 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
6864 1.1 christos INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb);
6865 1.1 christos
6866 1.1 christos if (type == dns_rdatatype_any)
6867 1.1 christos return (ISC_R_NOTIMPLEMENTED);
6868 1.1 christos if (type == dns_rdatatype_rrsig && covers == 0)
6869 1.1 christos return (ISC_R_NOTIMPLEMENTED);
6870 1.1 christos
6871 1.1 christos newheader = new_rdataset(rbtdb, rbtdb->common.mctx);
6872 1.1 christos if (newheader == NULL)
6873 1.1 christos return (ISC_R_NOMEMORY);
6874 1.1 christos init_rdataset(rbtdb, newheader);
6875 1.1 christos set_ttl(rbtdb, newheader, 0);
6876 1.1 christos newheader->type = RBTDB_RDATATYPE_VALUE(type, covers);
6877 1.1 christos newheader->attributes = RDATASET_ATTR_NONEXISTENT;
6878 1.1 christos newheader->trust = 0;
6879 1.1 christos newheader->noqname = NULL;
6880 1.1 christos newheader->closest = NULL;
6881 1.1 christos if (rbtversion != NULL)
6882 1.1 christos newheader->serial = rbtversion->serial;
6883 1.1 christos else
6884 1.1 christos newheader->serial = 0;
6885 1.1 christos newheader->count = 0;
6886 1.1 christos newheader->last_used = 0;
6887 1.1 christos newheader->node = rbtnode;
6888 1.1 christos
6889 1.1 christos NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
6890 1.1 christos isc_rwlocktype_write);
6891 1.1 christos
6892 1.1 christos result = add32(rbtdb, rbtnode, rbtversion, newheader, DNS_DBADD_FORCE,
6893 1.3 christos false, NULL, 0);
6894 1.1 christos
6895 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
6896 1.1 christos isc_rwlocktype_write);
6897 1.1 christos
6898 1.1 christos /*
6899 1.1 christos * Update the zone's secure status. If version is non-NULL
6900 1.1 christos * this is deferred until closeversion() is called.
6901 1.1 christos */
6902 1.1 christos if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb))
6903 1.1 christos iszonesecure(db, rbtdb->current_version, rbtdb->origin_node);
6904 1.1 christos
6905 1.1 christos return (result);
6906 1.1 christos }
6907 1.1 christos
6908 1.1 christos /*
6909 1.1 christos * load a non-NSEC3 node in the main tree and optionally to the auxiliary NSEC
6910 1.1 christos */
6911 1.1 christos static isc_result_t
6912 1.1 christos loadnode(dns_rbtdb_t *rbtdb, const dns_name_t *name, dns_rbtnode_t **nodep,
6913 1.3 christos bool hasnsec)
6914 1.1 christos {
6915 1.1 christos isc_result_t noderesult, nsecresult, tmpresult;
6916 1.1 christos dns_rbtnode_t *nsecnode = NULL, *node = NULL;
6917 1.1 christos
6918 1.1 christos noderesult = dns_rbt_addnode(rbtdb->tree, name, &node);
6919 1.1 christos if (!hasnsec)
6920 1.1 christos goto done;
6921 1.1 christos if (noderesult == ISC_R_EXISTS) {
6922 1.1 christos /*
6923 1.1 christos * Add a node to the auxiliary NSEC tree for an old node
6924 1.1 christos * just now getting an NSEC record.
6925 1.1 christos */
6926 1.1 christos if (node->nsec == DNS_RBT_NSEC_HAS_NSEC)
6927 1.1 christos goto done;
6928 1.1 christos } else if (noderesult != ISC_R_SUCCESS)
6929 1.1 christos goto done;
6930 1.1 christos
6931 1.1 christos /*
6932 1.1 christos * Build the auxiliary tree for NSECs as we go.
6933 1.1 christos * This tree speeds searches for closest NSECs that would otherwise
6934 1.1 christos * need to examine many irrelevant nodes in large TLDs.
6935 1.1 christos *
6936 1.1 christos * Add nodes to the auxiliary tree after corresponding nodes have
6937 1.1 christos * been added to the main tree.
6938 1.1 christos */
6939 1.1 christos nsecresult = dns_rbt_addnode(rbtdb->nsec, name, &nsecnode);
6940 1.1 christos if (nsecresult == ISC_R_SUCCESS) {
6941 1.1 christos nsecnode->nsec = DNS_RBT_NSEC_NSEC;
6942 1.1 christos node->nsec = DNS_RBT_NSEC_HAS_NSEC;
6943 1.1 christos goto done;
6944 1.1 christos }
6945 1.1 christos
6946 1.1 christos if (nsecresult == ISC_R_EXISTS) {
6947 1.1 christos #if 1 /* 0 */
6948 1.1 christos isc_log_write(dns_lctx,
6949 1.1 christos DNS_LOGCATEGORY_DATABASE,
6950 1.1 christos DNS_LOGMODULE_CACHE,
6951 1.1 christos ISC_LOG_WARNING,
6952 1.1 christos "addnode: NSEC node already exists");
6953 1.1 christos #endif
6954 1.1 christos node->nsec = DNS_RBT_NSEC_HAS_NSEC;
6955 1.1 christos goto done;
6956 1.1 christos }
6957 1.1 christos
6958 1.1 christos if (noderesult == ISC_R_SUCCESS) {
6959 1.1 christos
6960 1.1 christos /*
6961 1.1 christos * Remove the node we just added above.
6962 1.1 christos */
6963 1.3 christos tmpresult = dns_rbt_deletenode(rbtdb->tree, node, false);
6964 1.1 christos if (tmpresult != ISC_R_SUCCESS) {
6965 1.1 christos isc_log_write(dns_lctx,
6966 1.1 christos DNS_LOGCATEGORY_DATABASE,
6967 1.1 christos DNS_LOGMODULE_CACHE,
6968 1.1 christos ISC_LOG_WARNING,
6969 1.1 christos "loading_addrdataset: "
6970 1.1 christos "dns_rbt_deletenode: %s after "
6971 1.1 christos "dns_rbt_addnode(NSEC): %s",
6972 1.1 christos isc_result_totext(tmpresult),
6973 1.1 christos isc_result_totext(noderesult));
6974 1.1 christos }
6975 1.1 christos }
6976 1.1 christos
6977 1.1 christos /*
6978 1.1 christos * Set the error condition to be returned.
6979 1.1 christos */
6980 1.1 christos noderesult = nsecresult;
6981 1.1 christos
6982 1.1 christos done:
6983 1.1 christos if (noderesult == ISC_R_SUCCESS || noderesult == ISC_R_EXISTS)
6984 1.1 christos *nodep = node;
6985 1.1 christos
6986 1.1 christos return (noderesult);
6987 1.1 christos }
6988 1.1 christos
6989 1.1 christos static isc_result_t
6990 1.1 christos loading_addrdataset(void *arg, const dns_name_t *name,
6991 1.1 christos dns_rdataset_t *rdataset)
6992 1.1 christos {
6993 1.1 christos rbtdb_load_t *loadctx = arg;
6994 1.1 christos dns_rbtdb_t *rbtdb = loadctx->rbtdb;
6995 1.1 christos dns_rbtnode_t *node;
6996 1.1 christos isc_result_t result;
6997 1.1 christos isc_region_t region;
6998 1.1 christos rdatasetheader_t *newheader;
6999 1.1 christos
7000 1.1 christos REQUIRE(rdataset->rdclass == rbtdb->common.rdclass);
7001 1.1 christos
7002 1.1 christos /*
7003 1.1 christos * This routine does no node locking. See comments in
7004 1.1 christos * 'load' below for more information on loading and
7005 1.1 christos * locking.
7006 1.1 christos */
7007 1.1 christos
7008 1.1 christos /*
7009 1.1 christos * SOA records are only allowed at top of zone.
7010 1.1 christos */
7011 1.1 christos if (rdataset->type == dns_rdatatype_soa &&
7012 1.1 christos !IS_CACHE(rbtdb) && !dns_name_equal(name, &rbtdb->common.origin))
7013 1.1 christos return (DNS_R_NOTZONETOP);
7014 1.1 christos
7015 1.1 christos if (rdataset->type != dns_rdatatype_nsec3 &&
7016 1.1 christos rdataset->covers != dns_rdatatype_nsec3)
7017 1.1 christos add_empty_wildcards(rbtdb, name);
7018 1.1 christos
7019 1.1 christos if (dns_name_iswildcard(name)) {
7020 1.1 christos /*
7021 1.1 christos * NS record owners cannot legally be wild cards.
7022 1.1 christos */
7023 1.1 christos if (rdataset->type == dns_rdatatype_ns)
7024 1.1 christos return (DNS_R_INVALIDNS);
7025 1.1 christos /*
7026 1.1 christos * NSEC3 record owners cannot legally be wild cards.
7027 1.1 christos */
7028 1.1 christos if (rdataset->type == dns_rdatatype_nsec3)
7029 1.1 christos return (DNS_R_INVALIDNSEC3);
7030 1.1 christos result = add_wildcard_magic(rbtdb, name);
7031 1.1 christos if (result != ISC_R_SUCCESS)
7032 1.1 christos return (result);
7033 1.1 christos }
7034 1.1 christos
7035 1.1 christos node = NULL;
7036 1.1 christos if (rdataset->type == dns_rdatatype_nsec3 ||
7037 1.1 christos rdataset->covers == dns_rdatatype_nsec3) {
7038 1.1 christos result = dns_rbt_addnode(rbtdb->nsec3, name, &node);
7039 1.1 christos if (result == ISC_R_SUCCESS)
7040 1.1 christos node->nsec = DNS_RBT_NSEC_NSEC3;
7041 1.1 christos } else if (rdataset->type == dns_rdatatype_nsec) {
7042 1.3 christos result = loadnode(rbtdb, name, &node, true);
7043 1.1 christos } else {
7044 1.3 christos result = loadnode(rbtdb, name, &node, false);
7045 1.1 christos }
7046 1.1 christos if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS)
7047 1.1 christos return (result);
7048 1.1 christos if (result == ISC_R_SUCCESS) {
7049 1.1 christos dns_name_t foundname;
7050 1.1 christos dns_name_init(&foundname, NULL);
7051 1.1 christos dns_rbt_namefromnode(node, &foundname);
7052 1.1 christos node->locknum = node->hashval % rbtdb->node_lock_count;
7053 1.1 christos }
7054 1.1 christos
7055 1.1 christos result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx,
7056 1.1 christos ®ion,
7057 1.1 christos sizeof(rdatasetheader_t));
7058 1.1 christos if (result != ISC_R_SUCCESS)
7059 1.1 christos return (result);
7060 1.1 christos newheader = (rdatasetheader_t *)region.base;
7061 1.1 christos init_rdataset(rbtdb, newheader);
7062 1.1 christos set_ttl(rbtdb, newheader,
7063 1.1 christos rdataset->ttl + loadctx->now); /* XXX overflow check */
7064 1.1 christos newheader->type = RBTDB_RDATATYPE_VALUE(rdataset->type,
7065 1.1 christos rdataset->covers);
7066 1.1 christos newheader->attributes = 0;
7067 1.1 christos newheader->trust = rdataset->trust;
7068 1.1 christos newheader->serial = 1;
7069 1.1 christos newheader->noqname = NULL;
7070 1.1 christos newheader->closest = NULL;
7071 1.1 christos newheader->count = init_count++;
7072 1.1 christos newheader->last_used = 0;
7073 1.1 christos newheader->node = node;
7074 1.1 christos setownercase(newheader, name);
7075 1.1 christos
7076 1.1 christos if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) {
7077 1.1 christos newheader->attributes |= RDATASET_ATTR_RESIGN;
7078 1.1 christos newheader->resign = (isc_stdtime_t)
7079 1.1 christos (dns_time64_from32(rdataset->resign) >> 1);
7080 1.1 christos newheader->resign_lsb = rdataset->resign & 0x1;
7081 1.1 christos } else {
7082 1.1 christos newheader->resign = 0;
7083 1.1 christos newheader->resign_lsb = 0;
7084 1.1 christos }
7085 1.1 christos
7086 1.1 christos result = add32(rbtdb, node, rbtdb->current_version, newheader,
7087 1.3 christos DNS_DBADD_MERGE, true, NULL, 0);
7088 1.1 christos if (result == ISC_R_SUCCESS &&
7089 1.1 christos delegating_type(rbtdb, node, rdataset->type))
7090 1.1 christos node->find_callback = 1;
7091 1.1 christos else if (result == DNS_R_UNCHANGED)
7092 1.1 christos result = ISC_R_SUCCESS;
7093 1.1 christos
7094 1.1 christos return (result);
7095 1.1 christos }
7096 1.1 christos
7097 1.1 christos static isc_result_t
7098 1.1 christos rbt_datafixer(dns_rbtnode_t *rbtnode, void *base, size_t filesize,
7099 1.3 christos void *arg, uint64_t *crc)
7100 1.1 christos {
7101 1.1 christos isc_result_t result;
7102 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *) arg;
7103 1.1 christos rdatasetheader_t *header;
7104 1.1 christos unsigned char *limit = ((unsigned char *) base) + filesize;
7105 1.1 christos unsigned char *p;
7106 1.1 christos size_t size;
7107 1.1 christos unsigned int count;
7108 1.1 christos
7109 1.1 christos REQUIRE(rbtnode != NULL);
7110 1.1 christos
7111 1.1 christos for (header = rbtnode->data; header != NULL; header = header->next) {
7112 1.1 christos p = (unsigned char *) header;
7113 1.1 christos
7114 1.1 christos size = dns_rdataslab_size(p, sizeof(*header));
7115 1.1 christos count = dns_rdataslab_count(p, sizeof(*header));;
7116 1.1 christos rbtdb->current_version->records += count;
7117 1.1 christos rbtdb->current_version->bytes += size;
7118 1.1 christos isc_crc64_update(crc, p, size);
7119 1.1 christos #ifdef DEBUG
7120 1.1 christos hexdump("hashing header", p, sizeof(rdatasetheader_t));
7121 1.1 christos hexdump("hashing slab", p + sizeof(rdatasetheader_t),
7122 1.1 christos size - sizeof(rdatasetheader_t));
7123 1.1 christos #endif
7124 1.1 christos header->serial = 1;
7125 1.1 christos header->is_mmapped = 1;
7126 1.1 christos header->node = rbtnode;
7127 1.1 christos header->node_is_relative = 0;
7128 1.1 christos
7129 1.1 christos if (rbtdb != NULL && RESIGN(header) &&
7130 1.1 christos (header->resign != 0 || header->resign_lsb != 0))
7131 1.1 christos {
7132 1.1 christos int idx = header->node->locknum;
7133 1.1 christos result = isc_heap_insert(rbtdb->heaps[idx], header);
7134 1.1 christos if (result != ISC_R_SUCCESS)
7135 1.1 christos return (result);
7136 1.1 christos }
7137 1.1 christos
7138 1.1 christos if (header->next != NULL) {
7139 1.1 christos size_t cooked = dns_rbt_serialize_align(size);
7140 1.1 christos if ((uintptr_t)header->next !=
7141 1.1 christos (p - (unsigned char *)base) + cooked)
7142 1.1 christos return (ISC_R_INVALIDFILE);
7143 1.1 christos header->next = (rdatasetheader_t *)(p + cooked);
7144 1.1 christos header->next_is_relative = 0;
7145 1.1 christos if ((header->next < (rdatasetheader_t *) base) ||
7146 1.1 christos (header->next > (rdatasetheader_t *) limit))
7147 1.1 christos return (ISC_R_INVALIDFILE);
7148 1.1 christos }
7149 1.1 christos }
7150 1.1 christos
7151 1.1 christos return (ISC_R_SUCCESS);
7152 1.1 christos }
7153 1.1 christos
7154 1.1 christos /*
7155 1.1 christos * Load the RBT database from the image in 'f'
7156 1.1 christos */
7157 1.1 christos static isc_result_t
7158 1.1 christos deserialize32(void *arg, FILE *f, off_t offset) {
7159 1.1 christos isc_result_t result;
7160 1.1 christos rbtdb_load_t *loadctx = arg;
7161 1.1 christos dns_rbtdb_t *rbtdb = loadctx->rbtdb;
7162 1.1 christos rbtdb_file_header_t *header;
7163 1.1 christos int fd;
7164 1.1 christos off_t filesize = 0;
7165 1.1 christos char *base;
7166 1.1 christos dns_rbt_t *tree = NULL, *nsec = NULL, *nsec3 = NULL;
7167 1.1 christos int protect, flags;
7168 1.1 christos dns_rbtnode_t *origin_node = NULL;
7169 1.1 christos
7170 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7171 1.1 christos
7172 1.1 christos /*
7173 1.1 christos * TODO CKB: since this is read-write (had to be to add nodes later)
7174 1.1 christos * we will need to lock the file or the nodes in it before modifying
7175 1.1 christos * the nodes in the file.
7176 1.1 christos */
7177 1.1 christos
7178 1.1 christos /* Map in the whole file in one go */
7179 1.1 christos fd = fileno(f);
7180 1.1 christos isc_file_getsizefd(fd, &filesize);
7181 1.1 christos protect = PROT_READ|PROT_WRITE;
7182 1.1 christos flags = MAP_PRIVATE;
7183 1.1 christos #ifdef MAP_FILE
7184 1.1 christos flags |= MAP_FILE;
7185 1.1 christos #endif
7186 1.1 christos
7187 1.1 christos base = isc_file_mmap(NULL, filesize, protect, flags, fd, 0);
7188 1.1 christos if (base == NULL || base == MAP_FAILED) {
7189 1.1 christos return (ISC_R_FAILURE);
7190 1.1 christos }
7191 1.1 christos
7192 1.1 christos header = (rbtdb_file_header_t *)(base + offset);
7193 1.1 christos if (!match_header_version(header)) {
7194 1.1 christos result = ISC_R_INVALIDFILE;
7195 1.1 christos goto cleanup;
7196 1.1 christos }
7197 1.1 christos
7198 1.1 christos if (header->tree != 0) {
7199 1.1 christos result = dns_rbt_deserialize_tree(base, filesize,
7200 1.1 christos (off_t) header->tree,
7201 1.1 christos rbtdb->common.mctx,
7202 1.1 christos delete_callback, rbtdb,
7203 1.1 christos rbt_datafixer, rbtdb,
7204 1.1 christos NULL, &tree);
7205 1.1 christos if (result != ISC_R_SUCCESS)
7206 1.1 christos goto cleanup;
7207 1.1 christos
7208 1.1 christos result = dns_rbt_findnode(tree, &rbtdb->common.origin, NULL,
7209 1.1 christos &origin_node, NULL,
7210 1.1 christos DNS_RBTFIND_EMPTYDATA, NULL, NULL);
7211 1.1 christos if (result != ISC_R_SUCCESS)
7212 1.1 christos goto cleanup;
7213 1.1 christos }
7214 1.1 christos
7215 1.1 christos if (header->nsec != 0) {
7216 1.1 christos result = dns_rbt_deserialize_tree(base, filesize,
7217 1.1 christos (off_t) header->nsec,
7218 1.1 christos rbtdb->common.mctx,
7219 1.1 christos delete_callback, rbtdb,
7220 1.1 christos rbt_datafixer, rbtdb,
7221 1.1 christos NULL, &nsec);
7222 1.1 christos if (result != ISC_R_SUCCESS)
7223 1.1 christos goto cleanup;
7224 1.1 christos }
7225 1.1 christos
7226 1.1 christos if (header->nsec3 != 0) {
7227 1.1 christos result = dns_rbt_deserialize_tree(base, filesize,
7228 1.1 christos (off_t) header->nsec3,
7229 1.1 christos rbtdb->common.mctx,
7230 1.1 christos delete_callback, rbtdb,
7231 1.1 christos rbt_datafixer, rbtdb,
7232 1.1 christos NULL, &nsec3);
7233 1.1 christos if (result != ISC_R_SUCCESS)
7234 1.1 christos goto cleanup;
7235 1.1 christos }
7236 1.1 christos
7237 1.1 christos /*
7238 1.1 christos * We have a successfully loaded all the rbt trees now update
7239 1.1 christos * rbtdb to use them.
7240 1.1 christos */
7241 1.1 christos
7242 1.1 christos rbtdb->mmap_location = base;
7243 1.1 christos rbtdb->mmap_size = (size_t) filesize;
7244 1.1 christos
7245 1.1 christos if (tree != NULL) {
7246 1.1 christos dns_rbt_destroy(&rbtdb->tree);
7247 1.1 christos rbtdb->tree = tree;
7248 1.1 christos rbtdb->origin_node = origin_node;
7249 1.1 christos }
7250 1.1 christos
7251 1.1 christos if (nsec != NULL) {
7252 1.1 christos dns_rbt_destroy(&rbtdb->nsec);
7253 1.1 christos rbtdb->nsec = nsec;
7254 1.1 christos }
7255 1.1 christos
7256 1.1 christos if (nsec3 != NULL) {
7257 1.1 christos dns_rbt_destroy(&rbtdb->nsec3);
7258 1.1 christos rbtdb->nsec3 = nsec3;
7259 1.1 christos }
7260 1.1 christos
7261 1.1 christos return (ISC_R_SUCCESS);
7262 1.1 christos
7263 1.1 christos cleanup:
7264 1.1 christos if (tree != NULL)
7265 1.1 christos dns_rbt_destroy(&tree);
7266 1.1 christos if (nsec != NULL)
7267 1.1 christos dns_rbt_destroy(&nsec);
7268 1.1 christos if (nsec3 != NULL)
7269 1.1 christos dns_rbt_destroy(&nsec3);
7270 1.1 christos isc_file_munmap(base, (size_t) filesize);
7271 1.1 christos return (result);
7272 1.1 christos }
7273 1.1 christos
7274 1.1 christos static isc_result_t
7275 1.1 christos beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
7276 1.1 christos rbtdb_load_t *loadctx;
7277 1.1 christos dns_rbtdb_t *rbtdb;
7278 1.1 christos rbtdb = (dns_rbtdb_t *)db;
7279 1.1 christos
7280 1.1 christos REQUIRE(DNS_CALLBACK_VALID(callbacks));
7281 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7282 1.1 christos
7283 1.1 christos loadctx = isc_mem_get(rbtdb->common.mctx, sizeof(*loadctx));
7284 1.1 christos if (loadctx == NULL)
7285 1.1 christos return (ISC_R_NOMEMORY);
7286 1.1 christos
7287 1.1 christos loadctx->rbtdb = rbtdb;
7288 1.1 christos if (IS_CACHE(rbtdb))
7289 1.1 christos isc_stdtime_get(&loadctx->now);
7290 1.1 christos else
7291 1.1 christos loadctx->now = 0;
7292 1.1 christos
7293 1.1 christos RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
7294 1.1 christos
7295 1.1 christos REQUIRE((rbtdb->attributes & (RBTDB_ATTR_LOADED|RBTDB_ATTR_LOADING))
7296 1.1 christos == 0);
7297 1.1 christos rbtdb->attributes |= RBTDB_ATTR_LOADING;
7298 1.1 christos
7299 1.1 christos RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
7300 1.1 christos
7301 1.1 christos callbacks->add = loading_addrdataset;
7302 1.1 christos callbacks->add_private = loadctx;
7303 1.1 christos callbacks->deserialize = deserialize32;
7304 1.1 christos callbacks->deserialize_private = loadctx;
7305 1.1 christos
7306 1.1 christos return (ISC_R_SUCCESS);
7307 1.1 christos }
7308 1.1 christos
7309 1.1 christos static isc_result_t
7310 1.1 christos endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
7311 1.1 christos rbtdb_load_t *loadctx;
7312 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
7313 1.1 christos
7314 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7315 1.1 christos REQUIRE(DNS_CALLBACK_VALID(callbacks));
7316 1.1 christos loadctx = callbacks->add_private;
7317 1.1 christos REQUIRE(loadctx != NULL);
7318 1.1 christos REQUIRE(loadctx->rbtdb == rbtdb);
7319 1.1 christos
7320 1.1 christos RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
7321 1.1 christos
7322 1.1 christos REQUIRE((rbtdb->attributes & RBTDB_ATTR_LOADING) != 0);
7323 1.1 christos REQUIRE((rbtdb->attributes & RBTDB_ATTR_LOADED) == 0);
7324 1.1 christos
7325 1.1 christos rbtdb->attributes &= ~RBTDB_ATTR_LOADING;
7326 1.1 christos rbtdb->attributes |= RBTDB_ATTR_LOADED;
7327 1.1 christos
7328 1.1 christos RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
7329 1.1 christos
7330 1.1 christos /*
7331 1.1 christos * If there's a KEY rdataset at the zone origin containing a
7332 1.1 christos * zone key, we consider the zone secure.
7333 1.1 christos */
7334 1.1 christos if (! IS_CACHE(rbtdb) && rbtdb->origin_node != NULL)
7335 1.1 christos iszonesecure(db, rbtdb->current_version, rbtdb->origin_node);
7336 1.1 christos
7337 1.1 christos callbacks->add = NULL;
7338 1.1 christos callbacks->add_private = NULL;
7339 1.1 christos callbacks->deserialize = NULL;
7340 1.1 christos callbacks->deserialize_private = NULL;
7341 1.1 christos
7342 1.1 christos isc_mem_put(rbtdb->common.mctx, loadctx, sizeof(*loadctx));
7343 1.1 christos
7344 1.1 christos return (ISC_R_SUCCESS);
7345 1.1 christos }
7346 1.1 christos
7347 1.1 christos /*
7348 1.1 christos * helper function to handle writing out the rdataset data pointed to
7349 1.1 christos * by the void *data pointer in the dns_rbtnode
7350 1.1 christos */
7351 1.1 christos static isc_result_t
7352 1.1 christos rbt_datawriter(FILE *rbtfile, unsigned char *data, void *arg,
7353 1.3 christos uint64_t *crc)
7354 1.1 christos {
7355 1.1 christos rbtdb_version_t *version = (rbtdb_version_t *) arg;
7356 1.1 christos rbtdb_serial_t serial;
7357 1.1 christos rdatasetheader_t newheader;
7358 1.1 christos rdatasetheader_t *header = (rdatasetheader_t *) data, *next;
7359 1.1 christos off_t where;
7360 1.1 christos size_t cooked, size;
7361 1.1 christos unsigned char *p;
7362 1.1 christos isc_result_t result = ISC_R_SUCCESS;
7363 1.1 christos char pad[sizeof(char *)];
7364 1.1 christos uintptr_t off;
7365 1.1 christos
7366 1.1 christos REQUIRE(rbtfile != NULL);
7367 1.1 christos REQUIRE(data != NULL);
7368 1.1 christos REQUIRE(version != NULL);
7369 1.1 christos
7370 1.1 christos serial = version->serial;
7371 1.1 christos
7372 1.1 christos for (; header != NULL; header = next) {
7373 1.1 christos next = header->next;
7374 1.1 christos do {
7375 1.1 christos if (header->serial <= serial && !IGNORE(header)) {
7376 1.1 christos if (NONEXISTENT(header))
7377 1.1 christos header = NULL;
7378 1.1 christos break;
7379 1.1 christos } else
7380 1.1 christos header = header->down;
7381 1.1 christos } while (header != NULL);
7382 1.1 christos
7383 1.1 christos if (header == NULL)
7384 1.1 christos continue;
7385 1.1 christos
7386 1.1 christos CHECK(isc_stdio_tell(rbtfile, &where));
7387 1.1 christos size = dns_rdataslab_size((unsigned char *) header,
7388 1.1 christos sizeof(rdatasetheader_t));
7389 1.1 christos
7390 1.1 christos p = (unsigned char *) header;
7391 1.1 christos memmove(&newheader, p, sizeof(rdatasetheader_t));
7392 1.1 christos newheader.down = NULL;
7393 1.1 christos newheader.next = NULL;
7394 1.1 christos off = where;
7395 1.1 christos if ((off_t)off != where)
7396 1.1 christos return (ISC_R_RANGE);
7397 1.1 christos newheader.node = (dns_rbtnode_t *) off;
7398 1.1 christos newheader.node_is_relative = 1;
7399 1.1 christos newheader.serial = 1;
7400 1.1 christos
7401 1.1 christos /*
7402 1.1 christos * Round size up to the next pointer sized offset so it
7403 1.1 christos * will be properly aligned when read back in.
7404 1.1 christos */
7405 1.1 christos cooked = dns_rbt_serialize_align(size);
7406 1.1 christos if (next != NULL) {
7407 1.1 christos newheader.next = (rdatasetheader_t *) (off + cooked);
7408 1.1 christos newheader.next_is_relative = 1;
7409 1.1 christos }
7410 1.1 christos
7411 1.1 christos #ifdef DEBUG
7412 1.1 christos hexdump("writing header", (unsigned char *) &newheader,
7413 1.1 christos sizeof(rdatasetheader_t));
7414 1.1 christos hexdump("writing slab", p + sizeof(rdatasetheader_t),
7415 1.1 christos size - sizeof(rdatasetheader_t));
7416 1.1 christos #endif
7417 1.1 christos isc_crc64_update(crc, (unsigned char *) &newheader,
7418 1.1 christos sizeof(rdatasetheader_t));
7419 1.1 christos CHECK(isc_stdio_write(&newheader, sizeof(rdatasetheader_t), 1,
7420 1.1 christos rbtfile, NULL));
7421 1.1 christos
7422 1.1 christos isc_crc64_update(crc, p + sizeof(rdatasetheader_t),
7423 1.1 christos size - sizeof(rdatasetheader_t));
7424 1.1 christos CHECK(isc_stdio_write(p + sizeof(rdatasetheader_t),
7425 1.1 christos size - sizeof(rdatasetheader_t), 1,
7426 1.1 christos rbtfile, NULL));
7427 1.1 christos /*
7428 1.1 christos * Pad to force alignment.
7429 1.1 christos */
7430 1.1 christos if (size != (size_t) cooked) {
7431 1.1 christos memset(pad, 0, sizeof(pad));
7432 1.1 christos CHECK(isc_stdio_write(pad, cooked - size, 1,
7433 1.1 christos rbtfile, NULL));
7434 1.1 christos }
7435 1.1 christos }
7436 1.1 christos
7437 1.1 christos failure:
7438 1.1 christos return (result);
7439 1.1 christos }
7440 1.1 christos
7441 1.1 christos /*
7442 1.1 christos * Write out a zeroed header as a placeholder. Doing this ensures
7443 1.1 christos * that the file will not read while it is partially written, should
7444 1.1 christos * writing fail or be interrupted.
7445 1.1 christos */
7446 1.1 christos static isc_result_t
7447 1.1 christos rbtdb_zero_header(FILE *rbtfile) {
7448 1.1 christos char buffer[RBTDB_HEADER_LENGTH];
7449 1.1 christos isc_result_t result;
7450 1.1 christos
7451 1.1 christos memset(buffer, 0, RBTDB_HEADER_LENGTH);
7452 1.1 christos result = isc_stdio_write(buffer, 1, RBTDB_HEADER_LENGTH, rbtfile, NULL);
7453 1.1 christos fflush(rbtfile);
7454 1.1 christos
7455 1.1 christos return (result);
7456 1.1 christos }
7457 1.1 christos
7458 1.1 christos static isc_once_t once = ISC_ONCE_INIT;
7459 1.1 christos
7460 1.1 christos static void
7461 1.1 christos init_file_version(void) {
7462 1.1 christos int n;
7463 1.1 christos
7464 1.1 christos memset(FILE_VERSION, 0, sizeof(FILE_VERSION));
7465 1.1 christos n = snprintf(FILE_VERSION, sizeof(FILE_VERSION),
7466 1.1 christos "RBTDB Image %s %s", dns_major, dns_mapapi);
7467 1.1 christos INSIST(n > 0 && (unsigned int)n < sizeof(FILE_VERSION));
7468 1.1 christos }
7469 1.1 christos
7470 1.1 christos /*
7471 1.1 christos * Write the file header out, recording the locations of the three
7472 1.1 christos * RBT's used in the rbtdb: tree, nsec, and nsec3, and including NodeDump
7473 1.1 christos * version information and any information stored in the rbtdb object
7474 1.1 christos * itself that should be stored here.
7475 1.1 christos */
7476 1.1 christos static isc_result_t
7477 1.1 christos rbtdb_write_header(FILE *rbtfile, off_t tree_location, off_t nsec_location,
7478 1.1 christos off_t nsec3_location)
7479 1.1 christos {
7480 1.1 christos rbtdb_file_header_t header;
7481 1.1 christos isc_result_t result;
7482 1.1 christos
7483 1.1 christos RUNTIME_CHECK(isc_once_do(&once, init_file_version) == ISC_R_SUCCESS);
7484 1.1 christos
7485 1.1 christos memset(&header, 0, sizeof(rbtdb_file_header_t));
7486 1.1 christos memmove(header.version1, FILE_VERSION, sizeof(header.version1));
7487 1.1 christos memmove(header.version2, FILE_VERSION, sizeof(header.version2));
7488 1.3 christos header.ptrsize = (uint32_t) sizeof(void *);
7489 1.1 christos header.bigendian = (1 == htonl(1)) ? 1 : 0;
7490 1.3 christos header.tree = (uint64_t) tree_location;
7491 1.3 christos header.nsec = (uint64_t) nsec_location;
7492 1.3 christos header.nsec3 = (uint64_t) nsec3_location;
7493 1.1 christos result = isc_stdio_write(&header, 1, sizeof(rbtdb_file_header_t),
7494 1.1 christos rbtfile, NULL);
7495 1.1 christos fflush(rbtfile);
7496 1.1 christos
7497 1.1 christos return (result);
7498 1.1 christos }
7499 1.1 christos
7500 1.3 christos static bool
7501 1.1 christos match_header_version(rbtdb_file_header_t *header) {
7502 1.1 christos RUNTIME_CHECK(isc_once_do(&once, init_file_version) == ISC_R_SUCCESS);
7503 1.1 christos
7504 1.1 christos if (memcmp(header->version1, FILE_VERSION,
7505 1.1 christos sizeof(header->version1)) != 0 ||
7506 1.1 christos memcmp(header->version2, FILE_VERSION,
7507 1.1 christos sizeof(header->version1)) != 0)
7508 1.1 christos {
7509 1.3 christos return (false);
7510 1.1 christos }
7511 1.1 christos
7512 1.3 christos return (true);
7513 1.1 christos }
7514 1.1 christos
7515 1.1 christos static isc_result_t
7516 1.1 christos serialize(dns_db_t *db, dns_dbversion_t *ver, FILE *rbtfile) {
7517 1.1 christos rbtdb_version_t *version = (rbtdb_version_t *) ver;
7518 1.1 christos dns_rbtdb_t *rbtdb;
7519 1.1 christos isc_result_t result;
7520 1.1 christos off_t tree_location, nsec_location, nsec3_location, header_location;
7521 1.1 christos
7522 1.1 christos rbtdb = (dns_rbtdb_t *)db;
7523 1.1 christos
7524 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7525 1.1 christos REQUIRE(rbtfile != NULL);
7526 1.1 christos
7527 1.1 christos /* Ensure we're writing to a plain file */
7528 1.1 christos CHECK(isc_file_isplainfilefd(fileno(rbtfile)));
7529 1.1 christos
7530 1.1 christos /*
7531 1.1 christos * first, write out a zeroed header to store rbtdb information
7532 1.1 christos *
7533 1.1 christos * then for each of the three trees, store the current position
7534 1.1 christos * in the file and call dns_rbt_serialize_tree
7535 1.1 christos *
7536 1.1 christos * finally, write out the rbtdb header, storing the locations of the
7537 1.1 christos * rbtheaders
7538 1.1 christos *
7539 1.1 christos * NOTE: need to do something better with the return codes, &= will
7540 1.1 christos * not work.
7541 1.1 christos */
7542 1.1 christos CHECK(isc_stdio_tell(rbtfile, &header_location));
7543 1.1 christos CHECK(rbtdb_zero_header(rbtfile));
7544 1.1 christos CHECK(dns_rbt_serialize_tree(rbtfile, rbtdb->tree, rbt_datawriter,
7545 1.1 christos version, &tree_location));
7546 1.1 christos CHECK(dns_rbt_serialize_tree(rbtfile, rbtdb->nsec, rbt_datawriter,
7547 1.1 christos version, &nsec_location));
7548 1.1 christos CHECK(dns_rbt_serialize_tree(rbtfile, rbtdb->nsec3, rbt_datawriter,
7549 1.1 christos version, &nsec3_location));
7550 1.1 christos
7551 1.1 christos CHECK(isc_stdio_seek(rbtfile, header_location, SEEK_SET));
7552 1.1 christos CHECK(rbtdb_write_header(rbtfile, tree_location, nsec_location,
7553 1.1 christos nsec3_location));
7554 1.1 christos failure:
7555 1.1 christos return (result);
7556 1.1 christos }
7557 1.1 christos
7558 1.1 christos static isc_result_t
7559 1.1 christos dump(dns_db_t *db, dns_dbversion_t *version, const char *filename,
7560 1.1 christos dns_masterformat_t masterformat)
7561 1.1 christos {
7562 1.1 christos dns_rbtdb_t *rbtdb;
7563 1.1 christos rbtdb_version_t *rbtversion = version;
7564 1.1 christos
7565 1.1 christos rbtdb = (dns_rbtdb_t *)db;
7566 1.1 christos
7567 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7568 1.1 christos INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb);
7569 1.1 christos
7570 1.3 christos return (dns_master_dump(rbtdb->common.mctx, db, version,
7571 1.3 christos &dns_master_style_default,
7572 1.3 christos filename, masterformat, NULL));
7573 1.1 christos }
7574 1.1 christos
7575 1.1 christos static void
7576 1.1 christos delete_callback(void *data, void *arg) {
7577 1.1 christos dns_rbtdb_t *rbtdb = arg;
7578 1.1 christos rdatasetheader_t *current, *next;
7579 1.1 christos unsigned int locknum;
7580 1.1 christos
7581 1.1 christos current = data;
7582 1.1 christos locknum = current->node->locknum;
7583 1.1 christos NODE_LOCK(&rbtdb->node_locks[locknum].lock, isc_rwlocktype_write);
7584 1.1 christos while (current != NULL) {
7585 1.1 christos next = current->next;
7586 1.1 christos free_rdataset(rbtdb, rbtdb->common.mctx, current);
7587 1.1 christos current = next;
7588 1.1 christos }
7589 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, isc_rwlocktype_write);
7590 1.1 christos }
7591 1.1 christos
7592 1.3 christos static bool
7593 1.1 christos issecure(dns_db_t *db) {
7594 1.1 christos dns_rbtdb_t *rbtdb;
7595 1.3 christos bool secure;
7596 1.1 christos
7597 1.1 christos rbtdb = (dns_rbtdb_t *)db;
7598 1.1 christos
7599 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7600 1.1 christos
7601 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
7602 1.3 christos secure = (rbtdb->current_version->secure == dns_db_secure);
7603 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
7604 1.1 christos
7605 1.1 christos return (secure);
7606 1.1 christos }
7607 1.1 christos
7608 1.3 christos static bool
7609 1.1 christos isdnssec(dns_db_t *db) {
7610 1.1 christos dns_rbtdb_t *rbtdb;
7611 1.3 christos bool dnssec;
7612 1.1 christos
7613 1.1 christos rbtdb = (dns_rbtdb_t *)db;
7614 1.1 christos
7615 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7616 1.1 christos
7617 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
7618 1.3 christos dnssec = (rbtdb->current_version->secure != dns_db_insecure);
7619 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
7620 1.1 christos
7621 1.1 christos return (dnssec);
7622 1.1 christos }
7623 1.1 christos
7624 1.1 christos static unsigned int
7625 1.1 christos nodecount(dns_db_t *db) {
7626 1.1 christos dns_rbtdb_t *rbtdb;
7627 1.1 christos unsigned int count;
7628 1.1 christos
7629 1.1 christos rbtdb = (dns_rbtdb_t *)db;
7630 1.1 christos
7631 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7632 1.1 christos
7633 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
7634 1.1 christos count = dns_rbt_nodecount(rbtdb->tree);
7635 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
7636 1.1 christos
7637 1.1 christos return (count);
7638 1.1 christos }
7639 1.1 christos
7640 1.1 christos static size_t
7641 1.1 christos hashsize(dns_db_t *db) {
7642 1.1 christos dns_rbtdb_t *rbtdb;
7643 1.1 christos size_t size;
7644 1.1 christos
7645 1.1 christos rbtdb = (dns_rbtdb_t *)db;
7646 1.1 christos
7647 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7648 1.1 christos
7649 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
7650 1.1 christos size = dns_rbt_hashsize(rbtdb->tree);
7651 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
7652 1.1 christos
7653 1.1 christos return (size);
7654 1.1 christos }
7655 1.1 christos
7656 1.1 christos static void
7657 1.1 christos settask(dns_db_t *db, isc_task_t *task) {
7658 1.1 christos dns_rbtdb_t *rbtdb;
7659 1.1 christos
7660 1.1 christos rbtdb = (dns_rbtdb_t *)db;
7661 1.1 christos
7662 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7663 1.1 christos
7664 1.1 christos RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
7665 1.1 christos if (rbtdb->task != NULL)
7666 1.1 christos isc_task_detach(&rbtdb->task);
7667 1.1 christos if (task != NULL)
7668 1.1 christos isc_task_attach(task, &rbtdb->task);
7669 1.1 christos RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
7670 1.1 christos }
7671 1.1 christos
7672 1.3 christos static bool
7673 1.1 christos ispersistent(dns_db_t *db) {
7674 1.1 christos UNUSED(db);
7675 1.3 christos return (false);
7676 1.1 christos }
7677 1.1 christos
7678 1.1 christos static isc_result_t
7679 1.1 christos getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) {
7680 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
7681 1.1 christos dns_rbtnode_t *onode;
7682 1.1 christos isc_result_t result = ISC_R_SUCCESS;
7683 1.1 christos
7684 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7685 1.1 christos REQUIRE(nodep != NULL && *nodep == NULL);
7686 1.1 christos
7687 1.1 christos /* Note that the access to origin_node doesn't require a DB lock */
7688 1.1 christos onode = (dns_rbtnode_t *)rbtdb->origin_node;
7689 1.1 christos if (onode != NULL) {
7690 1.1 christos new_reference(rbtdb, onode);
7691 1.1 christos
7692 1.1 christos *nodep = rbtdb->origin_node;
7693 1.1 christos } else {
7694 1.1 christos INSIST(IS_CACHE(rbtdb));
7695 1.1 christos result = ISC_R_NOTFOUND;
7696 1.1 christos }
7697 1.1 christos
7698 1.1 christos return (result);
7699 1.1 christos }
7700 1.1 christos
7701 1.1 christos static isc_result_t
7702 1.1 christos getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, dns_hash_t *hash,
7703 1.3 christos uint8_t *flags, uint16_t *iterations,
7704 1.1 christos unsigned char *salt, size_t *salt_length)
7705 1.1 christos {
7706 1.1 christos dns_rbtdb_t *rbtdb;
7707 1.1 christos isc_result_t result = ISC_R_NOTFOUND;
7708 1.1 christos rbtdb_version_t *rbtversion = version;
7709 1.1 christos
7710 1.1 christos rbtdb = (dns_rbtdb_t *)db;
7711 1.1 christos
7712 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7713 1.1 christos INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb);
7714 1.1 christos
7715 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
7716 1.1 christos
7717 1.1 christos if (rbtversion == NULL)
7718 1.1 christos rbtversion = rbtdb->current_version;
7719 1.1 christos
7720 1.1 christos if (rbtversion->havensec3) {
7721 1.1 christos if (hash != NULL)
7722 1.1 christos *hash = rbtversion->hash;
7723 1.1 christos if (salt != NULL && salt_length != NULL) {
7724 1.1 christos REQUIRE(*salt_length >= rbtversion->salt_length);
7725 1.1 christos memmove(salt, rbtversion->salt,
7726 1.1 christos rbtversion->salt_length);
7727 1.1 christos }
7728 1.1 christos if (salt_length != NULL)
7729 1.1 christos *salt_length = rbtversion->salt_length;
7730 1.1 christos if (iterations != NULL)
7731 1.1 christos *iterations = rbtversion->iterations;
7732 1.1 christos if (flags != NULL)
7733 1.1 christos *flags = rbtversion->flags;
7734 1.1 christos result = ISC_R_SUCCESS;
7735 1.1 christos }
7736 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
7737 1.1 christos
7738 1.1 christos return (result);
7739 1.1 christos }
7740 1.1 christos
7741 1.1 christos static isc_result_t
7742 1.3 christos getsize(dns_db_t *db, dns_dbversion_t *version, uint64_t *records,
7743 1.3 christos uint64_t *bytes)
7744 1.1 christos {
7745 1.1 christos dns_rbtdb_t *rbtdb;
7746 1.1 christos isc_result_t result = ISC_R_SUCCESS;
7747 1.1 christos rbtdb_version_t *rbtversion = version;
7748 1.1 christos
7749 1.1 christos rbtdb = (dns_rbtdb_t *)db;
7750 1.1 christos
7751 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7752 1.1 christos INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb);
7753 1.1 christos
7754 1.1 christos if (rbtversion == NULL)
7755 1.1 christos rbtversion = rbtdb->current_version;
7756 1.1 christos
7757 1.1 christos RWLOCK(&rbtversion->rwlock, isc_rwlocktype_read);
7758 1.1 christos if (records != NULL)
7759 1.1 christos *records = rbtversion->records;
7760 1.1 christos
7761 1.1 christos if (bytes != NULL)
7762 1.1 christos *bytes = rbtversion->bytes;
7763 1.1 christos RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_read);
7764 1.1 christos
7765 1.1 christos return (result);
7766 1.1 christos }
7767 1.1 christos
7768 1.1 christos static isc_result_t
7769 1.1 christos setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) {
7770 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
7771 1.1 christos isc_result_t result = ISC_R_SUCCESS;
7772 1.1 christos rdatasetheader_t *header, oldheader;
7773 1.1 christos
7774 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7775 1.1 christos REQUIRE(!IS_CACHE(rbtdb));
7776 1.1 christos REQUIRE(rdataset != NULL);
7777 1.1 christos
7778 1.1 christos header = rdataset->private3;
7779 1.1 christos header--;
7780 1.1 christos
7781 1.1 christos NODE_LOCK(&rbtdb->node_locks[header->node->locknum].lock,
7782 1.1 christos isc_rwlocktype_write);
7783 1.1 christos
7784 1.1 christos oldheader = *header;
7785 1.1 christos /*
7786 1.1 christos * Only break the heap invariant (by adjusting resign and resign_lsb)
7787 1.1 christos * if we are going to be restoring it by calling isc_heap_increased
7788 1.1 christos * or isc_heap_decreased.
7789 1.1 christos */
7790 1.1 christos if (resign != 0) {
7791 1.1 christos header->resign =
7792 1.1 christos (isc_stdtime_t)(dns_time64_from32(resign) >> 1);
7793 1.1 christos header->resign_lsb = resign & 0x1;
7794 1.1 christos }
7795 1.1 christos if (header->heap_index != 0) {
7796 1.1 christos INSIST(RESIGN(header));
7797 1.1 christos if (resign == 0) {
7798 1.1 christos isc_heap_delete(rbtdb->heaps[header->node->locknum],
7799 1.1 christos header->heap_index);
7800 1.1 christos header->heap_index = 0;
7801 1.1 christos } else if (resign_sooner(header, &oldheader)) {
7802 1.1 christos isc_heap_increased(rbtdb->heaps[header->node->locknum],
7803 1.1 christos header->heap_index);
7804 1.1 christos } else if (resign_sooner(&oldheader, header)) {
7805 1.1 christos isc_heap_decreased(rbtdb->heaps[header->node->locknum],
7806 1.1 christos header->heap_index);
7807 1.1 christos }
7808 1.1 christos } else if (resign != 0) {
7809 1.1 christos header->attributes |= RDATASET_ATTR_RESIGN;
7810 1.1 christos result = resign_insert(rbtdb, header->node->locknum, header);
7811 1.1 christos }
7812 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[header->node->locknum].lock,
7813 1.1 christos isc_rwlocktype_write);
7814 1.1 christos return (result);
7815 1.1 christos }
7816 1.1 christos
7817 1.1 christos static isc_result_t
7818 1.1 christos getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset,
7819 1.1 christos dns_name_t *foundname)
7820 1.1 christos {
7821 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
7822 1.1 christos rdatasetheader_t *header = NULL, *this;
7823 1.1 christos unsigned int i;
7824 1.1 christos isc_result_t result = ISC_R_NOTFOUND;
7825 1.1 christos unsigned int locknum;
7826 1.1 christos
7827 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7828 1.1 christos
7829 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
7830 1.1 christos
7831 1.1 christos for (i = 0; i < rbtdb->node_lock_count; i++) {
7832 1.1 christos NODE_LOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_read);
7833 1.1 christos this = isc_heap_element(rbtdb->heaps[i], 1);
7834 1.1 christos if (this == NULL) {
7835 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[i].lock,
7836 1.1 christos isc_rwlocktype_read);
7837 1.1 christos continue;
7838 1.1 christos }
7839 1.1 christos if (header == NULL)
7840 1.1 christos header = this;
7841 1.1 christos else if (resign_sooner(this, header)) {
7842 1.1 christos locknum = header->node->locknum;
7843 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
7844 1.1 christos isc_rwlocktype_read);
7845 1.1 christos header = this;
7846 1.1 christos } else
7847 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[i].lock,
7848 1.1 christos isc_rwlocktype_read);
7849 1.1 christos }
7850 1.1 christos
7851 1.1 christos if (header == NULL)
7852 1.1 christos goto unlock;
7853 1.1 christos
7854 1.1 christos bind_rdataset(rbtdb, header->node, header, 0, rdataset);
7855 1.1 christos
7856 1.1 christos if (foundname != NULL)
7857 1.1 christos dns_rbt_fullnamefromnode(header->node, foundname);
7858 1.1 christos
7859 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[header->node->locknum].lock,
7860 1.1 christos isc_rwlocktype_read);
7861 1.1 christos
7862 1.1 christos result = ISC_R_SUCCESS;
7863 1.1 christos
7864 1.1 christos unlock:
7865 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
7866 1.1 christos
7867 1.1 christos return (result);
7868 1.1 christos }
7869 1.1 christos
7870 1.1 christos static void
7871 1.1 christos resigned(dns_db_t *db, dns_rdataset_t *rdataset, dns_dbversion_t *version)
7872 1.1 christos {
7873 1.1 christos rbtdb_version_t *rbtversion = (rbtdb_version_t *)version;
7874 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
7875 1.1 christos dns_rbtnode_t *node;
7876 1.1 christos rdatasetheader_t *header;
7877 1.1 christos
7878 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7879 1.1 christos REQUIRE(rdataset != NULL);
7880 1.1 christos REQUIRE(rdataset->methods == &rdataset_methods);
7881 1.1 christos REQUIRE(rbtdb->future_version == rbtversion);
7882 1.1 christos REQUIRE(rbtversion != NULL);
7883 1.1 christos REQUIRE(rbtversion->writer);
7884 1.1 christos REQUIRE(rbtversion->rbtdb == rbtdb);
7885 1.1 christos
7886 1.1 christos node = rdataset->private2;
7887 1.1 christos INSIST(node != NULL);
7888 1.1 christos header = rdataset->private3;
7889 1.1 christos INSIST(header != NULL);
7890 1.1 christos header--;
7891 1.1 christos
7892 1.1 christos if (header->heap_index == 0)
7893 1.1 christos return;
7894 1.1 christos
7895 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
7896 1.1 christos NODE_LOCK(&rbtdb->node_locks[node->locknum].lock,
7897 1.1 christos isc_rwlocktype_write);
7898 1.1 christos /*
7899 1.1 christos * Delete from heap and save to re-signed list so that it can
7900 1.1 christos * be restored if we backout of this change.
7901 1.1 christos */
7902 1.1 christos resign_delete(rbtdb, rbtversion, header);
7903 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[node->locknum].lock,
7904 1.1 christos isc_rwlocktype_write);
7905 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
7906 1.1 christos }
7907 1.1 christos
7908 1.1 christos static isc_result_t
7909 1.1 christos setcachestats(dns_db_t *db, isc_stats_t *stats) {
7910 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
7911 1.1 christos
7912 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7913 1.1 christos REQUIRE(IS_CACHE(rbtdb)); /* current restriction */
7914 1.1 christos REQUIRE(stats != NULL);
7915 1.1 christos
7916 1.1 christos isc_stats_attach(stats, &rbtdb->cachestats);
7917 1.1 christos return (ISC_R_SUCCESS);
7918 1.1 christos }
7919 1.1 christos
7920 1.1 christos static isc_result_t
7921 1.1 christos setgluecachestats(dns_db_t *db, isc_stats_t *stats) {
7922 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
7923 1.1 christos
7924 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7925 1.1 christos REQUIRE(!IS_CACHE(rbtdb) && !IS_STUB(rbtdb));
7926 1.1 christos REQUIRE(stats != NULL);
7927 1.1 christos
7928 1.1 christos isc_stats_attach(stats, &rbtdb->gluecachestats);
7929 1.1 christos return (ISC_R_SUCCESS);
7930 1.1 christos }
7931 1.1 christos
7932 1.1 christos static dns_stats_t *
7933 1.1 christos getrrsetstats(dns_db_t *db) {
7934 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
7935 1.1 christos
7936 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7937 1.1 christos REQUIRE(IS_CACHE(rbtdb)); /* current restriction */
7938 1.1 christos
7939 1.1 christos return (rbtdb->rrsetstats);
7940 1.1 christos }
7941 1.1 christos
7942 1.1 christos static isc_result_t
7943 1.1 christos nodefullname(dns_db_t *db, dns_dbnode_t *node, dns_name_t *name) {
7944 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
7945 1.1 christos dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
7946 1.1 christos isc_result_t result;
7947 1.1 christos
7948 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7949 1.1 christos REQUIRE(node != NULL);
7950 1.1 christos REQUIRE(name != NULL);
7951 1.1 christos
7952 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
7953 1.1 christos result = dns_rbt_fullnamefromnode(rbtnode, name);
7954 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
7955 1.1 christos
7956 1.1 christos return (result);
7957 1.1 christos }
7958 1.1 christos
7959 1.1 christos static isc_result_t
7960 1.1 christos setservestalettl(dns_db_t *db, dns_ttl_t ttl) {
7961 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
7962 1.1 christos
7963 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7964 1.1 christos REQUIRE(IS_CACHE(rbtdb));
7965 1.1 christos
7966 1.1 christos /* currently no bounds checking. 0 means disable. */
7967 1.1 christos rbtdb->serve_stale_ttl = ttl;
7968 1.1 christos return (ISC_R_SUCCESS);
7969 1.1 christos }
7970 1.1 christos
7971 1.1 christos static isc_result_t
7972 1.1 christos getservestalettl(dns_db_t *db, dns_ttl_t *ttl) {
7973 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
7974 1.1 christos
7975 1.1 christos REQUIRE(VALID_RBTDB(rbtdb));
7976 1.1 christos REQUIRE(IS_CACHE(rbtdb));
7977 1.1 christos
7978 1.1 christos *ttl = rbtdb->serve_stale_ttl;
7979 1.1 christos return (ISC_R_SUCCESS);
7980 1.1 christos }
7981 1.1 christos
7982 1.1 christos
7983 1.1 christos static dns_dbmethods_t zone_methods = {
7984 1.1 christos attach,
7985 1.1 christos detach,
7986 1.1 christos beginload,
7987 1.1 christos endload,
7988 1.1 christos serialize,
7989 1.1 christos dump,
7990 1.1 christos currentversion,
7991 1.1 christos newversion,
7992 1.1 christos attachversion,
7993 1.1 christos closeversion,
7994 1.1 christos findnode,
7995 1.1 christos zone_find,
7996 1.1 christos zone_findzonecut,
7997 1.1 christos attachnode,
7998 1.1 christos detachnode,
7999 1.1 christos expirenode,
8000 1.1 christos printnode,
8001 1.1 christos createiterator,
8002 1.1 christos zone_findrdataset,
8003 1.1 christos allrdatasets,
8004 1.1 christos addrdataset,
8005 1.1 christos subtractrdataset,
8006 1.1 christos deleterdataset,
8007 1.1 christos issecure,
8008 1.1 christos nodecount,
8009 1.1 christos ispersistent,
8010 1.1 christos overmem,
8011 1.1 christos settask,
8012 1.1 christos getoriginnode,
8013 1.1 christos NULL, /* transfernode */
8014 1.1 christos getnsec3parameters,
8015 1.1 christos findnsec3node,
8016 1.1 christos setsigningtime,
8017 1.1 christos getsigningtime,
8018 1.1 christos resigned,
8019 1.1 christos isdnssec,
8020 1.1 christos NULL, /* getrrsetstats */
8021 1.1 christos NULL, /* rpz_attach */
8022 1.1 christos NULL, /* rpz_ready */
8023 1.1 christos NULL, /* findnodeext */
8024 1.1 christos NULL, /* findext */
8025 1.1 christos NULL, /* setcachestats */
8026 1.1 christos hashsize,
8027 1.1 christos nodefullname,
8028 1.1 christos getsize,
8029 1.1 christos NULL, /* setservestalettl */
8030 1.1 christos NULL, /* getservestalettl */
8031 1.1 christos setgluecachestats
8032 1.1 christos };
8033 1.1 christos
8034 1.1 christos static dns_dbmethods_t cache_methods = {
8035 1.1 christos attach,
8036 1.1 christos detach,
8037 1.1 christos beginload,
8038 1.1 christos endload,
8039 1.1 christos NULL, /* serialize */
8040 1.1 christos dump,
8041 1.1 christos currentversion,
8042 1.1 christos newversion,
8043 1.1 christos attachversion,
8044 1.1 christos closeversion,
8045 1.1 christos findnode,
8046 1.1 christos cache_find,
8047 1.1 christos cache_findzonecut,
8048 1.1 christos attachnode,
8049 1.1 christos detachnode,
8050 1.1 christos expirenode,
8051 1.1 christos printnode,
8052 1.1 christos createiterator,
8053 1.1 christos cache_findrdataset,
8054 1.1 christos allrdatasets,
8055 1.1 christos addrdataset,
8056 1.1 christos subtractrdataset,
8057 1.1 christos deleterdataset,
8058 1.1 christos issecure,
8059 1.1 christos nodecount,
8060 1.1 christos ispersistent,
8061 1.1 christos overmem,
8062 1.1 christos settask,
8063 1.1 christos getoriginnode,
8064 1.1 christos NULL, /* transfernode */
8065 1.1 christos NULL, /* getnsec3parameters */
8066 1.1 christos NULL, /* findnsec3node */
8067 1.1 christos NULL, /* setsigningtime */
8068 1.1 christos NULL, /* getsigningtime */
8069 1.1 christos NULL, /* resigned */
8070 1.1 christos isdnssec,
8071 1.1 christos getrrsetstats,
8072 1.1 christos NULL, /* rpz_attach */
8073 1.1 christos NULL, /* rpz_ready */
8074 1.1 christos NULL, /* findnodeext */
8075 1.1 christos NULL, /* findext */
8076 1.1 christos setcachestats,
8077 1.1 christos hashsize,
8078 1.1 christos nodefullname,
8079 1.1 christos NULL, /* getsize */
8080 1.1 christos setservestalettl,
8081 1.1 christos getservestalettl,
8082 1.1 christos NULL
8083 1.1 christos };
8084 1.1 christos
8085 1.1 christos isc_result_t
8086 1.3 christos dns_rbtdb_create(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type,
8087 1.1 christos dns_rdataclass_t rdclass, unsigned int argc, char *argv[],
8088 1.1 christos void *driverarg, dns_db_t **dbp)
8089 1.1 christos {
8090 1.1 christos dns_rbtdb_t *rbtdb;
8091 1.1 christos isc_result_t result;
8092 1.1 christos int i;
8093 1.1 christos dns_name_t name;
8094 1.3 christos bool (*sooner)(void *, void *);
8095 1.1 christos isc_mem_t *hmctx = mctx;
8096 1.1 christos
8097 1.1 christos /* Keep the compiler happy. */
8098 1.1 christos UNUSED(driverarg);
8099 1.1 christos
8100 1.1 christos rbtdb = isc_mem_get(mctx, sizeof(*rbtdb));
8101 1.1 christos if (rbtdb == NULL)
8102 1.1 christos return (ISC_R_NOMEMORY);
8103 1.1 christos
8104 1.1 christos /*
8105 1.1 christos * If argv[0] exists, it points to a memory context to use for heap
8106 1.1 christos */
8107 1.1 christos if (argc != 0)
8108 1.1 christos hmctx = (isc_mem_t *) argv[0];
8109 1.1 christos
8110 1.1 christos memset(rbtdb, '\0', sizeof(*rbtdb));
8111 1.1 christos dns_name_init(&rbtdb->common.origin, NULL);
8112 1.1 christos rbtdb->common.attributes = 0;
8113 1.1 christos if (type == dns_dbtype_cache) {
8114 1.1 christos rbtdb->common.methods = &cache_methods;
8115 1.1 christos rbtdb->common.attributes |= DNS_DBATTR_CACHE;
8116 1.1 christos } else if (type == dns_dbtype_stub) {
8117 1.1 christos rbtdb->common.methods = &zone_methods;
8118 1.1 christos rbtdb->common.attributes |= DNS_DBATTR_STUB;
8119 1.1 christos } else
8120 1.1 christos rbtdb->common.methods = &zone_methods;
8121 1.1 christos rbtdb->common.rdclass = rdclass;
8122 1.1 christos rbtdb->common.mctx = NULL;
8123 1.1 christos
8124 1.1 christos ISC_LIST_INIT(rbtdb->common.update_listeners);
8125 1.1 christos
8126 1.1 christos result = RBTDB_INITLOCK(&rbtdb->lock);
8127 1.1 christos if (result != ISC_R_SUCCESS)
8128 1.1 christos goto cleanup_rbtdb;
8129 1.1 christos
8130 1.1 christos result = isc_rwlock_init(&rbtdb->tree_lock, 0, 0);
8131 1.1 christos if (result != ISC_R_SUCCESS)
8132 1.1 christos goto cleanup_lock;
8133 1.1 christos
8134 1.1 christos /*
8135 1.1 christos * Initialize node_lock_count in a generic way to support future
8136 1.1 christos * extension which allows the user to specify this value on creation.
8137 1.1 christos * Note that when specified for a cache DB it must be larger than 1
8138 1.1 christos * as commented with the definition of DEFAULT_CACHE_NODE_LOCK_COUNT.
8139 1.1 christos */
8140 1.1 christos if (rbtdb->node_lock_count == 0) {
8141 1.1 christos if (IS_CACHE(rbtdb))
8142 1.1 christos rbtdb->node_lock_count = DEFAULT_CACHE_NODE_LOCK_COUNT;
8143 1.1 christos else
8144 1.1 christos rbtdb->node_lock_count = DEFAULT_NODE_LOCK_COUNT;
8145 1.1 christos } else if (rbtdb->node_lock_count < 2 && IS_CACHE(rbtdb)) {
8146 1.1 christos result = ISC_R_RANGE;
8147 1.1 christos goto cleanup_tree_lock;
8148 1.1 christos }
8149 1.1 christos INSIST(rbtdb->node_lock_count < (1 << DNS_RBT_LOCKLENGTH));
8150 1.1 christos rbtdb->node_locks = isc_mem_get(mctx, rbtdb->node_lock_count *
8151 1.1 christos sizeof(rbtdb_nodelock_t));
8152 1.1 christos if (rbtdb->node_locks == NULL) {
8153 1.1 christos result = ISC_R_NOMEMORY;
8154 1.1 christos goto cleanup_tree_lock;
8155 1.1 christos }
8156 1.1 christos
8157 1.1 christos rbtdb->cachestats = NULL;
8158 1.1 christos rbtdb->gluecachestats = NULL;
8159 1.1 christos
8160 1.1 christos rbtdb->rrsetstats = NULL;
8161 1.1 christos if (IS_CACHE(rbtdb)) {
8162 1.1 christos result = dns_rdatasetstats_create(mctx, &rbtdb->rrsetstats);
8163 1.1 christos if (result != ISC_R_SUCCESS)
8164 1.1 christos goto cleanup_node_locks;
8165 1.1 christos rbtdb->rdatasets = isc_mem_get(mctx, rbtdb->node_lock_count *
8166 1.1 christos sizeof(rdatasetheaderlist_t));
8167 1.1 christos if (rbtdb->rdatasets == NULL) {
8168 1.1 christos result = ISC_R_NOMEMORY;
8169 1.1 christos goto cleanup_rrsetstats;
8170 1.1 christos }
8171 1.1 christos for (i = 0; i < (int)rbtdb->node_lock_count; i++)
8172 1.1 christos ISC_LIST_INIT(rbtdb->rdatasets[i]);
8173 1.1 christos } else
8174 1.1 christos rbtdb->rdatasets = NULL;
8175 1.1 christos
8176 1.1 christos /*
8177 1.1 christos * Create the heaps.
8178 1.1 christos */
8179 1.1 christos rbtdb->heaps = isc_mem_get(hmctx, rbtdb->node_lock_count *
8180 1.1 christos sizeof(isc_heap_t *));
8181 1.1 christos if (rbtdb->heaps == NULL) {
8182 1.1 christos result = ISC_R_NOMEMORY;
8183 1.1 christos goto cleanup_rdatasets;
8184 1.1 christos }
8185 1.1 christos for (i = 0; i < (int)rbtdb->node_lock_count; i++)
8186 1.1 christos rbtdb->heaps[i] = NULL;
8187 1.1 christos sooner = IS_CACHE(rbtdb) ? ttl_sooner : resign_sooner;
8188 1.1 christos for (i = 0; i < (int)rbtdb->node_lock_count; i++) {
8189 1.1 christos result = isc_heap_create(hmctx, sooner, set_index, 0,
8190 1.1 christos &rbtdb->heaps[i]);
8191 1.1 christos if (result != ISC_R_SUCCESS)
8192 1.1 christos goto cleanup_heaps;
8193 1.1 christos }
8194 1.1 christos
8195 1.1 christos /*
8196 1.1 christos * Create deadnode lists.
8197 1.1 christos */
8198 1.1 christos rbtdb->deadnodes = isc_mem_get(mctx, rbtdb->node_lock_count *
8199 1.1 christos sizeof(rbtnodelist_t));
8200 1.1 christos if (rbtdb->deadnodes == NULL) {
8201 1.1 christos result = ISC_R_NOMEMORY;
8202 1.1 christos goto cleanup_heaps;
8203 1.1 christos }
8204 1.1 christos for (i = 0; i < (int)rbtdb->node_lock_count; i++)
8205 1.1 christos ISC_LIST_INIT(rbtdb->deadnodes[i]);
8206 1.1 christos
8207 1.1 christos rbtdb->active = rbtdb->node_lock_count;
8208 1.1 christos
8209 1.1 christos for (i = 0; i < (int)(rbtdb->node_lock_count); i++) {
8210 1.1 christos result = NODE_INITLOCK(&rbtdb->node_locks[i].lock);
8211 1.1 christos if (result == ISC_R_SUCCESS) {
8212 1.3 christos isc_refcount_init(&rbtdb->node_locks[i].references, 0);
8213 1.1 christos }
8214 1.1 christos if (result != ISC_R_SUCCESS) {
8215 1.1 christos while (i-- > 0) {
8216 1.1 christos NODE_DESTROYLOCK(&rbtdb->node_locks[i].lock);
8217 1.1 christos isc_refcount_destroy(&rbtdb->node_locks[i].references);
8218 1.1 christos }
8219 1.1 christos goto cleanup_deadnodes;
8220 1.1 christos }
8221 1.3 christos rbtdb->node_locks[i].exiting = false;
8222 1.1 christos }
8223 1.1 christos
8224 1.1 christos /*
8225 1.1 christos * Attach to the mctx. The database will persist so long as there
8226 1.1 christos * are references to it, and attaching to the mctx ensures that our
8227 1.1 christos * mctx won't disappear out from under us.
8228 1.1 christos */
8229 1.1 christos isc_mem_attach(mctx, &rbtdb->common.mctx);
8230 1.1 christos isc_mem_attach(hmctx, &rbtdb->hmctx);
8231 1.1 christos
8232 1.1 christos /*
8233 1.1 christos * Make a copy of the origin name.
8234 1.1 christos */
8235 1.1 christos result = dns_name_dupwithoffsets(origin, mctx, &rbtdb->common.origin);
8236 1.1 christos if (result != ISC_R_SUCCESS) {
8237 1.3 christos free_rbtdb(rbtdb, false, NULL);
8238 1.1 christos return (result);
8239 1.1 christos }
8240 1.1 christos
8241 1.1 christos /*
8242 1.1 christos * Make the Red-Black Trees.
8243 1.1 christos */
8244 1.1 christos result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->tree);
8245 1.1 christos if (result != ISC_R_SUCCESS) {
8246 1.3 christos free_rbtdb(rbtdb, false, NULL);
8247 1.1 christos return (result);
8248 1.1 christos }
8249 1.1 christos
8250 1.1 christos result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->nsec);
8251 1.1 christos if (result != ISC_R_SUCCESS) {
8252 1.3 christos free_rbtdb(rbtdb, false, NULL);
8253 1.1 christos return (result);
8254 1.1 christos }
8255 1.1 christos
8256 1.1 christos result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->nsec3);
8257 1.1 christos if (result != ISC_R_SUCCESS) {
8258 1.3 christos free_rbtdb(rbtdb, false, NULL);
8259 1.1 christos return (result);
8260 1.1 christos }
8261 1.1 christos
8262 1.1 christos /*
8263 1.1 christos * In order to set the node callback bit correctly in zone databases,
8264 1.1 christos * we need to know if the node has the origin name of the zone.
8265 1.1 christos * In loading_addrdataset() we could simply compare the new name
8266 1.1 christos * to the origin name, but this is expensive. Also, we don't know the
8267 1.1 christos * node name in addrdataset(), so we need another way of knowing the
8268 1.1 christos * zone's top.
8269 1.1 christos *
8270 1.1 christos * We now explicitly create a node for the zone's origin, and then
8271 1.1 christos * we simply remember the node's address. This is safe, because
8272 1.1 christos * the top-of-zone node can never be deleted, nor can its address
8273 1.1 christos * change.
8274 1.1 christos */
8275 1.1 christos if (!IS_CACHE(rbtdb)) {
8276 1.1 christos rbtdb->origin_node = NULL;
8277 1.1 christos result = dns_rbt_addnode(rbtdb->tree, &rbtdb->common.origin,
8278 1.1 christos &rbtdb->origin_node);
8279 1.1 christos if (result != ISC_R_SUCCESS) {
8280 1.1 christos INSIST(result != ISC_R_EXISTS);
8281 1.3 christos free_rbtdb(rbtdb, false, NULL);
8282 1.1 christos return (result);
8283 1.1 christos }
8284 1.1 christos INSIST(rbtdb->origin_node != NULL);
8285 1.1 christos rbtdb->origin_node->nsec = DNS_RBT_NSEC_NORMAL;
8286 1.1 christos /*
8287 1.1 christos * We need to give the origin node the right locknum.
8288 1.1 christos */
8289 1.1 christos dns_name_init(&name, NULL);
8290 1.1 christos dns_rbt_namefromnode(rbtdb->origin_node, &name);
8291 1.1 christos rbtdb->origin_node->locknum =
8292 1.1 christos rbtdb->origin_node->hashval %
8293 1.1 christos rbtdb->node_lock_count;
8294 1.1 christos /*
8295 1.1 christos * Add an apex node to the NSEC3 tree so that NSEC3 searches
8296 1.1 christos * return partial matches when there is only a single NSEC3
8297 1.1 christos * record in the tree.
8298 1.1 christos */
8299 1.1 christos rbtdb->nsec3_origin_node = NULL;
8300 1.1 christos result = dns_rbt_addnode(rbtdb->nsec3, &rbtdb->common.origin,
8301 1.1 christos &rbtdb->nsec3_origin_node);
8302 1.1 christos if (result != ISC_R_SUCCESS) {
8303 1.1 christos INSIST(result != ISC_R_EXISTS);
8304 1.3 christos free_rbtdb(rbtdb, false, NULL);
8305 1.1 christos return (result);
8306 1.1 christos }
8307 1.1 christos rbtdb->nsec3_origin_node->nsec = DNS_RBT_NSEC_NSEC3;
8308 1.1 christos /*
8309 1.1 christos * We need to give the nsec3 origin node the right locknum.
8310 1.1 christos */
8311 1.1 christos dns_name_init(&name, NULL);
8312 1.1 christos dns_rbt_namefromnode(rbtdb->nsec3_origin_node, &name);
8313 1.1 christos rbtdb->nsec3_origin_node->locknum =
8314 1.1 christos rbtdb->nsec3_origin_node->hashval %
8315 1.1 christos rbtdb->node_lock_count;
8316 1.1 christos }
8317 1.1 christos
8318 1.1 christos /*
8319 1.1 christos * Misc. Initialization.
8320 1.1 christos */
8321 1.3 christos isc_refcount_init(&rbtdb->references, 1);
8322 1.1 christos rbtdb->attributes = 0;
8323 1.1 christos rbtdb->task = NULL;
8324 1.1 christos rbtdb->serve_stale_ttl = 0;
8325 1.1 christos
8326 1.1 christos /*
8327 1.1 christos * Version Initialization.
8328 1.1 christos */
8329 1.1 christos rbtdb->current_serial = 1;
8330 1.1 christos rbtdb->least_serial = 1;
8331 1.1 christos rbtdb->next_serial = 2;
8332 1.3 christos rbtdb->current_version = allocate_version(mctx, 1, 1, false);
8333 1.1 christos if (rbtdb->current_version == NULL) {
8334 1.3 christos INSIST(isc_refcount_decrement(&rbtdb->references) > 0);
8335 1.3 christos free_rbtdb(rbtdb, false, NULL);
8336 1.1 christos return (ISC_R_NOMEMORY);
8337 1.1 christos }
8338 1.1 christos rbtdb->current_version->rbtdb = rbtdb;
8339 1.1 christos rbtdb->current_version->secure = dns_db_insecure;
8340 1.3 christos rbtdb->current_version->havensec3 = false;
8341 1.1 christos rbtdb->current_version->flags = 0;
8342 1.1 christos rbtdb->current_version->iterations = 0;
8343 1.1 christos rbtdb->current_version->hash = 0;
8344 1.1 christos rbtdb->current_version->salt_length = 0;
8345 1.1 christos memset(rbtdb->current_version->salt, 0,
8346 1.1 christos sizeof(rbtdb->current_version->salt));
8347 1.1 christos result = isc_rwlock_init(&rbtdb->current_version->rwlock, 0, 0);
8348 1.1 christos if (result != ISC_R_SUCCESS) {
8349 1.1 christos free_gluetable(rbtdb->current_version);
8350 1.1 christos isc_rwlock_destroy(&rbtdb->current_version->glue_rwlock);
8351 1.1 christos isc_refcount_destroy(&rbtdb->current_version->references);
8352 1.1 christos isc_mem_put(mctx, rbtdb->current_version,
8353 1.1 christos sizeof(*rbtdb->current_version));
8354 1.1 christos rbtdb->current_version = NULL;
8355 1.3 christos INSIST(isc_refcount_decrement(&rbtdb->references) > 0);
8356 1.3 christos free_rbtdb(rbtdb, false, NULL);
8357 1.1 christos return (result);
8358 1.1 christos }
8359 1.1 christos
8360 1.1 christos rbtdb->current_version->records = 0;
8361 1.1 christos rbtdb->current_version->bytes = 0;
8362 1.1 christos rbtdb->future_version = NULL;
8363 1.1 christos ISC_LIST_INIT(rbtdb->open_versions);
8364 1.1 christos /*
8365 1.1 christos * Keep the current version in the open list so that list operation
8366 1.1 christos * won't happen in normal lookup operations.
8367 1.1 christos */
8368 1.1 christos PREPEND(rbtdb->open_versions, rbtdb->current_version, link);
8369 1.1 christos
8370 1.1 christos rbtdb->common.magic = DNS_DB_MAGIC;
8371 1.1 christos rbtdb->common.impmagic = RBTDB_MAGIC;
8372 1.1 christos
8373 1.1 christos *dbp = (dns_db_t *)rbtdb;
8374 1.1 christos
8375 1.1 christos return (ISC_R_SUCCESS);
8376 1.1 christos
8377 1.1 christos cleanup_deadnodes:
8378 1.1 christos isc_mem_put(mctx, rbtdb->deadnodes,
8379 1.1 christos rbtdb->node_lock_count * sizeof(rbtnodelist_t));
8380 1.1 christos
8381 1.1 christos cleanup_heaps:
8382 1.1 christos if (rbtdb->heaps != NULL) {
8383 1.1 christos for (i = 0 ; i < (int)rbtdb->node_lock_count ; i++)
8384 1.1 christos if (rbtdb->heaps[i] != NULL)
8385 1.1 christos isc_heap_destroy(&rbtdb->heaps[i]);
8386 1.1 christos isc_mem_put(hmctx, rbtdb->heaps,
8387 1.1 christos rbtdb->node_lock_count * sizeof(isc_heap_t *));
8388 1.1 christos }
8389 1.1 christos
8390 1.1 christos cleanup_rdatasets:
8391 1.1 christos if (rbtdb->rdatasets != NULL)
8392 1.1 christos isc_mem_put(mctx, rbtdb->rdatasets, rbtdb->node_lock_count *
8393 1.1 christos sizeof(rdatasetheaderlist_t));
8394 1.1 christos cleanup_rrsetstats:
8395 1.1 christos if (rbtdb->rrsetstats != NULL)
8396 1.1 christos dns_stats_detach(&rbtdb->rrsetstats);
8397 1.1 christos
8398 1.1 christos cleanup_node_locks:
8399 1.1 christos isc_mem_put(mctx, rbtdb->node_locks,
8400 1.1 christos rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t));
8401 1.1 christos
8402 1.1 christos cleanup_tree_lock:
8403 1.1 christos isc_rwlock_destroy(&rbtdb->tree_lock);
8404 1.1 christos
8405 1.1 christos cleanup_lock:
8406 1.1 christos RBTDB_DESTROYLOCK(&rbtdb->lock);
8407 1.1 christos
8408 1.1 christos cleanup_rbtdb:
8409 1.1 christos isc_mem_put(mctx, rbtdb, sizeof(*rbtdb));
8410 1.1 christos return (result);
8411 1.1 christos }
8412 1.1 christos
8413 1.1 christos
8414 1.1 christos /*
8415 1.1 christos * Slabbed Rdataset Methods
8416 1.1 christos */
8417 1.1 christos
8418 1.1 christos static void
8419 1.1 christos rdataset_disassociate(dns_rdataset_t *rdataset) {
8420 1.1 christos dns_db_t *db = rdataset->private1;
8421 1.1 christos dns_dbnode_t *node = rdataset->private2;
8422 1.1 christos
8423 1.1 christos detachnode(db, &node);
8424 1.1 christos }
8425 1.1 christos
8426 1.1 christos static isc_result_t
8427 1.1 christos rdataset_first(dns_rdataset_t *rdataset) {
8428 1.1 christos unsigned char *raw = rdataset->private3; /* RDATASLAB */
8429 1.1 christos unsigned int count;
8430 1.1 christos
8431 1.1 christos count = raw[0] * 256 + raw[1];
8432 1.1 christos if (count == 0) {
8433 1.1 christos rdataset->private5 = NULL;
8434 1.1 christos return (ISC_R_NOMORE);
8435 1.1 christos }
8436 1.1 christos
8437 1.1 christos #if DNS_RDATASET_FIXED
8438 1.1 christos if ((rdataset->attributes & DNS_RDATASETATTR_LOADORDER) == 0)
8439 1.1 christos raw += 2 + (4 * count);
8440 1.1 christos else
8441 1.1 christos #endif
8442 1.1 christos raw += 2;
8443 1.1 christos
8444 1.1 christos /*
8445 1.1 christos * The privateuint4 field is the number of rdata beyond the
8446 1.1 christos * cursor position, so we decrement the total count by one
8447 1.1 christos * before storing it.
8448 1.1 christos *
8449 1.1 christos * If DNS_RDATASETATTR_LOADORDER is not set 'raw' points to the
8450 1.1 christos * first record. If DNS_RDATASETATTR_LOADORDER is set 'raw' points
8451 1.1 christos * to the first entry in the offset table.
8452 1.1 christos */
8453 1.1 christos count--;
8454 1.1 christos rdataset->privateuint4 = count;
8455 1.1 christos rdataset->private5 = raw;
8456 1.1 christos
8457 1.1 christos return (ISC_R_SUCCESS);
8458 1.1 christos }
8459 1.1 christos
8460 1.1 christos static isc_result_t
8461 1.1 christos rdataset_next(dns_rdataset_t *rdataset) {
8462 1.1 christos unsigned int count;
8463 1.1 christos unsigned int length;
8464 1.1 christos unsigned char *raw; /* RDATASLAB */
8465 1.1 christos
8466 1.1 christos count = rdataset->privateuint4;
8467 1.1 christos if (count == 0)
8468 1.1 christos return (ISC_R_NOMORE);
8469 1.1 christos count--;
8470 1.1 christos rdataset->privateuint4 = count;
8471 1.1 christos
8472 1.1 christos /*
8473 1.1 christos * Skip forward one record (length + 4) or one offset (4).
8474 1.1 christos */
8475 1.1 christos raw = rdataset->private5;
8476 1.1 christos #if DNS_RDATASET_FIXED
8477 1.1 christos if ((rdataset->attributes & DNS_RDATASETATTR_LOADORDER) == 0) {
8478 1.1 christos #endif
8479 1.1 christos length = raw[0] * 256 + raw[1];
8480 1.1 christos raw += length;
8481 1.1 christos #if DNS_RDATASET_FIXED
8482 1.1 christos }
8483 1.1 christos rdataset->private5 = raw + 4; /* length(2) + order(2) */
8484 1.1 christos #else
8485 1.1 christos rdataset->private5 = raw + 2; /* length(2) */
8486 1.1 christos #endif
8487 1.1 christos
8488 1.1 christos return (ISC_R_SUCCESS);
8489 1.1 christos }
8490 1.1 christos
8491 1.1 christos static void
8492 1.1 christos rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
8493 1.1 christos unsigned char *raw = rdataset->private5; /* RDATASLAB */
8494 1.1 christos #if DNS_RDATASET_FIXED
8495 1.1 christos unsigned int offset;
8496 1.1 christos #endif
8497 1.1 christos unsigned int length;
8498 1.1 christos isc_region_t r;
8499 1.1 christos unsigned int flags = 0;
8500 1.1 christos
8501 1.1 christos REQUIRE(raw != NULL);
8502 1.1 christos
8503 1.1 christos /*
8504 1.1 christos * Find the start of the record if not already in private5
8505 1.1 christos * then skip the length and order fields.
8506 1.1 christos */
8507 1.1 christos #if DNS_RDATASET_FIXED
8508 1.1 christos if ((rdataset->attributes & DNS_RDATASETATTR_LOADORDER) != 0) {
8509 1.1 christos offset = (raw[0] << 24) + (raw[1] << 16) +
8510 1.1 christos (raw[2] << 8) + raw[3];
8511 1.1 christos raw = rdataset->private3;
8512 1.1 christos raw += offset;
8513 1.1 christos }
8514 1.1 christos #endif
8515 1.1 christos length = raw[0] * 256 + raw[1];
8516 1.1 christos #if DNS_RDATASET_FIXED
8517 1.1 christos raw += 4;
8518 1.1 christos #else
8519 1.1 christos raw += 2;
8520 1.1 christos #endif
8521 1.1 christos if (rdataset->type == dns_rdatatype_rrsig) {
8522 1.1 christos if (*raw & DNS_RDATASLAB_OFFLINE)
8523 1.1 christos flags |= DNS_RDATA_OFFLINE;
8524 1.1 christos length--;
8525 1.1 christos raw++;
8526 1.1 christos }
8527 1.1 christos r.length = length;
8528 1.1 christos r.base = raw;
8529 1.1 christos dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r);
8530 1.1 christos rdata->flags |= flags;
8531 1.1 christos }
8532 1.1 christos
8533 1.1 christos static void
8534 1.1 christos rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
8535 1.1 christos dns_db_t *db = source->private1;
8536 1.1 christos dns_dbnode_t *node = source->private2;
8537 1.1 christos dns_dbnode_t *cloned_node = NULL;
8538 1.1 christos
8539 1.1 christos attachnode(db, node, &cloned_node);
8540 1.1 christos INSIST(!ISC_LINK_LINKED(target, link));
8541 1.1 christos *target = *source;
8542 1.1 christos ISC_LINK_INIT(target, link);
8543 1.1 christos
8544 1.1 christos /*
8545 1.1 christos * Reset iterator state.
8546 1.1 christos */
8547 1.1 christos target->privateuint4 = 0;
8548 1.1 christos target->private5 = NULL;
8549 1.1 christos }
8550 1.1 christos
8551 1.1 christos static unsigned int
8552 1.1 christos rdataset_count(dns_rdataset_t *rdataset) {
8553 1.1 christos unsigned char *raw = rdataset->private3; /* RDATASLAB */
8554 1.1 christos unsigned int count;
8555 1.1 christos
8556 1.1 christos count = raw[0] * 256 + raw[1];
8557 1.1 christos
8558 1.1 christos return (count);
8559 1.1 christos }
8560 1.1 christos
8561 1.1 christos static isc_result_t
8562 1.1 christos rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
8563 1.1 christos dns_rdataset_t *nsec, dns_rdataset_t *nsecsig)
8564 1.1 christos {
8565 1.1 christos dns_db_t *db = rdataset->private1;
8566 1.1 christos dns_dbnode_t *node = rdataset->private2;
8567 1.1 christos dns_dbnode_t *cloned_node;
8568 1.1 christos const struct noqname *noqname = rdataset->private6;
8569 1.1 christos
8570 1.1 christos cloned_node = NULL;
8571 1.1 christos attachnode(db, node, &cloned_node);
8572 1.1 christos nsec->methods = &slab_methods;
8573 1.1 christos nsec->rdclass = db->rdclass;
8574 1.1 christos nsec->type = noqname->type;
8575 1.1 christos nsec->covers = 0;
8576 1.1 christos nsec->ttl = rdataset->ttl;
8577 1.1 christos nsec->trust = rdataset->trust;
8578 1.1 christos nsec->private1 = rdataset->private1;
8579 1.1 christos nsec->private2 = rdataset->private2;
8580 1.1 christos nsec->private3 = noqname->neg;
8581 1.1 christos nsec->privateuint4 = 0;
8582 1.1 christos nsec->private5 = NULL;
8583 1.1 christos nsec->private6 = NULL;
8584 1.1 christos nsec->private7 = NULL;
8585 1.1 christos
8586 1.1 christos cloned_node = NULL;
8587 1.1 christos attachnode(db, node, &cloned_node);
8588 1.1 christos nsecsig->methods = &slab_methods;
8589 1.1 christos nsecsig->rdclass = db->rdclass;
8590 1.1 christos nsecsig->type = dns_rdatatype_rrsig;
8591 1.1 christos nsecsig->covers = noqname->type;
8592 1.1 christos nsecsig->ttl = rdataset->ttl;
8593 1.1 christos nsecsig->trust = rdataset->trust;
8594 1.1 christos nsecsig->private1 = rdataset->private1;
8595 1.1 christos nsecsig->private2 = rdataset->private2;
8596 1.1 christos nsecsig->private3 = noqname->negsig;
8597 1.1 christos nsecsig->privateuint4 = 0;
8598 1.1 christos nsecsig->private5 = NULL;
8599 1.1 christos nsec->private6 = NULL;
8600 1.1 christos nsec->private7 = NULL;
8601 1.1 christos
8602 1.1 christos dns_name_clone(&noqname->name, name);
8603 1.1 christos
8604 1.1 christos return (ISC_R_SUCCESS);
8605 1.1 christos }
8606 1.1 christos
8607 1.1 christos static isc_result_t
8608 1.1 christos rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
8609 1.1 christos dns_rdataset_t *nsec, dns_rdataset_t *nsecsig)
8610 1.1 christos {
8611 1.1 christos dns_db_t *db = rdataset->private1;
8612 1.1 christos dns_dbnode_t *node = rdataset->private2;
8613 1.1 christos dns_dbnode_t *cloned_node;
8614 1.1 christos const struct noqname *closest = rdataset->private7;
8615 1.1 christos
8616 1.1 christos cloned_node = NULL;
8617 1.1 christos attachnode(db, node, &cloned_node);
8618 1.1 christos nsec->methods = &slab_methods;
8619 1.1 christos nsec->rdclass = db->rdclass;
8620 1.1 christos nsec->type = closest->type;
8621 1.1 christos nsec->covers = 0;
8622 1.1 christos nsec->ttl = rdataset->ttl;
8623 1.1 christos nsec->trust = rdataset->trust;
8624 1.1 christos nsec->private1 = rdataset->private1;
8625 1.1 christos nsec->private2 = rdataset->private2;
8626 1.1 christos nsec->private3 = closest->neg;
8627 1.1 christos nsec->privateuint4 = 0;
8628 1.1 christos nsec->private5 = NULL;
8629 1.1 christos nsec->private6 = NULL;
8630 1.1 christos nsec->private7 = NULL;
8631 1.1 christos
8632 1.1 christos cloned_node = NULL;
8633 1.1 christos attachnode(db, node, &cloned_node);
8634 1.1 christos nsecsig->methods = &slab_methods;
8635 1.1 christos nsecsig->rdclass = db->rdclass;
8636 1.1 christos nsecsig->type = dns_rdatatype_rrsig;
8637 1.1 christos nsecsig->covers = closest->type;
8638 1.1 christos nsecsig->ttl = rdataset->ttl;
8639 1.1 christos nsecsig->trust = rdataset->trust;
8640 1.1 christos nsecsig->private1 = rdataset->private1;
8641 1.1 christos nsecsig->private2 = rdataset->private2;
8642 1.1 christos nsecsig->private3 = closest->negsig;
8643 1.1 christos nsecsig->privateuint4 = 0;
8644 1.1 christos nsecsig->private5 = NULL;
8645 1.1 christos nsec->private6 = NULL;
8646 1.1 christos nsec->private7 = NULL;
8647 1.1 christos
8648 1.1 christos dns_name_clone(&closest->name, name);
8649 1.1 christos
8650 1.1 christos return (ISC_R_SUCCESS);
8651 1.1 christos }
8652 1.1 christos
8653 1.1 christos static void
8654 1.1 christos rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
8655 1.1 christos dns_rbtdb_t *rbtdb = rdataset->private1;
8656 1.1 christos dns_rbtnode_t *rbtnode = rdataset->private2;
8657 1.1 christos rdatasetheader_t *header = rdataset->private3;
8658 1.1 christos
8659 1.1 christos header--;
8660 1.1 christos NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
8661 1.1 christos isc_rwlocktype_write);
8662 1.1 christos header->trust = rdataset->trust = trust;
8663 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
8664 1.1 christos isc_rwlocktype_write);
8665 1.1 christos }
8666 1.1 christos
8667 1.1 christos static void
8668 1.1 christos rdataset_expire(dns_rdataset_t *rdataset) {
8669 1.1 christos dns_rbtdb_t *rbtdb = rdataset->private1;
8670 1.1 christos dns_rbtnode_t *rbtnode = rdataset->private2;
8671 1.1 christos rdatasetheader_t *header = rdataset->private3;
8672 1.1 christos
8673 1.1 christos header--;
8674 1.1 christos NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
8675 1.1 christos isc_rwlocktype_write);
8676 1.3 christos expire_header(rbtdb, header, false, expire_flush);
8677 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
8678 1.1 christos isc_rwlocktype_write);
8679 1.1 christos }
8680 1.1 christos
8681 1.1 christos static void
8682 1.1 christos rdataset_clearprefetch(dns_rdataset_t *rdataset) {
8683 1.1 christos dns_rbtdb_t *rbtdb = rdataset->private1;
8684 1.1 christos dns_rbtnode_t *rbtnode = rdataset->private2;
8685 1.1 christos rdatasetheader_t *header = rdataset->private3;
8686 1.1 christos
8687 1.1 christos header--;
8688 1.1 christos NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
8689 1.1 christos isc_rwlocktype_write);
8690 1.1 christos header->attributes &= ~RDATASET_ATTR_PREFETCH;
8691 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
8692 1.1 christos isc_rwlocktype_write);
8693 1.1 christos }
8694 1.1 christos
8695 1.1 christos /*
8696 1.1 christos * Rdataset Iterator Methods
8697 1.1 christos */
8698 1.1 christos
8699 1.1 christos static void
8700 1.1 christos rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) {
8701 1.1 christos rbtdb_rdatasetiter_t *rbtiterator;
8702 1.1 christos
8703 1.1 christos rbtiterator = (rbtdb_rdatasetiter_t *)(*iteratorp);
8704 1.1 christos
8705 1.1 christos if (rbtiterator->common.version != NULL)
8706 1.1 christos closeversion(rbtiterator->common.db,
8707 1.3 christos &rbtiterator->common.version, false);
8708 1.1 christos detachnode(rbtiterator->common.db, &rbtiterator->common.node);
8709 1.1 christos isc_mem_put(rbtiterator->common.db->mctx, rbtiterator,
8710 1.1 christos sizeof(*rbtiterator));
8711 1.1 christos
8712 1.1 christos *iteratorp = NULL;
8713 1.1 christos }
8714 1.1 christos
8715 1.1 christos static isc_result_t
8716 1.1 christos rdatasetiter_first(dns_rdatasetiter_t *iterator) {
8717 1.1 christos rbtdb_rdatasetiter_t *rbtiterator = (rbtdb_rdatasetiter_t *)iterator;
8718 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db);
8719 1.1 christos dns_rbtnode_t *rbtnode = rbtiterator->common.node;
8720 1.1 christos rbtdb_version_t *rbtversion = rbtiterator->common.version;
8721 1.1 christos rdatasetheader_t *header, *top_next;
8722 1.1 christos rbtdb_serial_t serial;
8723 1.1 christos isc_stdtime_t now;
8724 1.1 christos
8725 1.1 christos if (IS_CACHE(rbtdb)) {
8726 1.1 christos serial = 1;
8727 1.1 christos now = rbtiterator->common.now;
8728 1.1 christos } else {
8729 1.1 christos serial = rbtversion->serial;
8730 1.1 christos now = 0;
8731 1.1 christos }
8732 1.1 christos
8733 1.1 christos NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
8734 1.1 christos isc_rwlocktype_read);
8735 1.1 christos
8736 1.1 christos for (header = rbtnode->data; header != NULL; header = top_next) {
8737 1.1 christos top_next = header->next;
8738 1.1 christos do {
8739 1.1 christos if (header->serial <= serial && !IGNORE(header)) {
8740 1.1 christos /*
8741 1.1 christos * Is this a "this rdataset doesn't exist"
8742 1.1 christos * record? Or is it too old in the cache?
8743 1.1 christos *
8744 1.1 christos * Note: unlike everywhere else, we
8745 1.1 christos * check for now > header->rdh_ttl instead
8746 1.1 christos * of now >= header->rdh_ttl. This allows
8747 1.1 christos * ANY and RRSIG queries for 0 TTL
8748 1.1 christos * rdatasets to work.
8749 1.1 christos */
8750 1.1 christos if (NONEXISTENT(header) ||
8751 1.1 christos (now != 0 && now > header->rdh_ttl
8752 1.1 christos + rbtdb->serve_stale_ttl))
8753 1.1 christos header = NULL;
8754 1.1 christos break;
8755 1.1 christos } else
8756 1.1 christos header = header->down;
8757 1.1 christos } while (header != NULL);
8758 1.1 christos if (header != NULL)
8759 1.1 christos break;
8760 1.1 christos }
8761 1.1 christos
8762 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
8763 1.1 christos isc_rwlocktype_read);
8764 1.1 christos
8765 1.1 christos rbtiterator->current = header;
8766 1.1 christos
8767 1.1 christos if (header == NULL)
8768 1.1 christos return (ISC_R_NOMORE);
8769 1.1 christos
8770 1.1 christos return (ISC_R_SUCCESS);
8771 1.1 christos }
8772 1.1 christos
8773 1.1 christos static isc_result_t
8774 1.1 christos rdatasetiter_next(dns_rdatasetiter_t *iterator) {
8775 1.1 christos rbtdb_rdatasetiter_t *rbtiterator = (rbtdb_rdatasetiter_t *)iterator;
8776 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db);
8777 1.1 christos dns_rbtnode_t *rbtnode = rbtiterator->common.node;
8778 1.1 christos rbtdb_version_t *rbtversion = rbtiterator->common.version;
8779 1.1 christos rdatasetheader_t *header, *top_next;
8780 1.1 christos rbtdb_serial_t serial;
8781 1.1 christos isc_stdtime_t now;
8782 1.1 christos rbtdb_rdatatype_t type, negtype;
8783 1.1 christos dns_rdatatype_t rdtype, covers;
8784 1.1 christos
8785 1.1 christos header = rbtiterator->current;
8786 1.1 christos if (header == NULL)
8787 1.1 christos return (ISC_R_NOMORE);
8788 1.1 christos
8789 1.1 christos if (IS_CACHE(rbtdb)) {
8790 1.1 christos serial = 1;
8791 1.1 christos now = rbtiterator->common.now;
8792 1.1 christos } else {
8793 1.1 christos serial = rbtversion->serial;
8794 1.1 christos now = 0;
8795 1.1 christos }
8796 1.1 christos
8797 1.1 christos NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
8798 1.1 christos isc_rwlocktype_read);
8799 1.1 christos
8800 1.1 christos type = header->type;
8801 1.1 christos rdtype = RBTDB_RDATATYPE_BASE(header->type);
8802 1.1 christos if (NEGATIVE(header)) {
8803 1.1 christos covers = RBTDB_RDATATYPE_EXT(header->type);
8804 1.1 christos negtype = RBTDB_RDATATYPE_VALUE(covers, 0);
8805 1.1 christos } else
8806 1.1 christos negtype = RBTDB_RDATATYPE_VALUE(0, rdtype);
8807 1.1 christos for (header = header->next; header != NULL; header = top_next) {
8808 1.1 christos top_next = header->next;
8809 1.1 christos /*
8810 1.1 christos * If not walking back up the down list.
8811 1.1 christos */
8812 1.1 christos if (header->type != type && header->type != negtype) {
8813 1.1 christos do {
8814 1.1 christos if (header->serial <= serial &&
8815 1.1 christos !IGNORE(header)) {
8816 1.1 christos /*
8817 1.1 christos * Is this a "this rdataset doesn't
8818 1.1 christos * exist" record?
8819 1.1 christos *
8820 1.1 christos * Note: unlike everywhere else, we
8821 1.1 christos * check for now > header->ttl instead
8822 1.1 christos * of now >= header->ttl. This allows
8823 1.1 christos * ANY and RRSIG queries for 0 TTL
8824 1.1 christos * rdatasets to work.
8825 1.1 christos */
8826 1.1 christos if (NONEXISTENT(header) ||
8827 1.1 christos (now != 0 && now > header->rdh_ttl))
8828 1.1 christos header = NULL;
8829 1.1 christos break;
8830 1.1 christos } else
8831 1.1 christos header = header->down;
8832 1.1 christos } while (header != NULL);
8833 1.1 christos if (header != NULL)
8834 1.1 christos break;
8835 1.1 christos }
8836 1.1 christos }
8837 1.1 christos
8838 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
8839 1.1 christos isc_rwlocktype_read);
8840 1.1 christos
8841 1.1 christos rbtiterator->current = header;
8842 1.1 christos
8843 1.1 christos if (header == NULL)
8844 1.1 christos return (ISC_R_NOMORE);
8845 1.1 christos
8846 1.1 christos return (ISC_R_SUCCESS);
8847 1.1 christos }
8848 1.1 christos
8849 1.1 christos static void
8850 1.1 christos rdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset) {
8851 1.1 christos rbtdb_rdatasetiter_t *rbtiterator = (rbtdb_rdatasetiter_t *)iterator;
8852 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db);
8853 1.1 christos dns_rbtnode_t *rbtnode = rbtiterator->common.node;
8854 1.1 christos rdatasetheader_t *header;
8855 1.1 christos
8856 1.1 christos header = rbtiterator->current;
8857 1.1 christos REQUIRE(header != NULL);
8858 1.1 christos
8859 1.1 christos NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
8860 1.1 christos isc_rwlocktype_read);
8861 1.1 christos
8862 1.1 christos bind_rdataset(rbtdb, rbtnode, header, rbtiterator->common.now,
8863 1.1 christos rdataset);
8864 1.1 christos
8865 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
8866 1.1 christos isc_rwlocktype_read);
8867 1.1 christos }
8868 1.1 christos
8869 1.1 christos
8870 1.1 christos /*
8871 1.1 christos * Database Iterator Methods
8872 1.1 christos */
8873 1.1 christos
8874 1.1 christos static inline void
8875 1.1 christos reference_iter_node(rbtdb_dbiterator_t *rbtdbiter) {
8876 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db;
8877 1.1 christos dns_rbtnode_t *node = rbtdbiter->node;
8878 1.1 christos
8879 1.1 christos if (node == NULL)
8880 1.1 christos return;
8881 1.1 christos
8882 1.1 christos INSIST(rbtdbiter->tree_locked != isc_rwlocktype_none);
8883 1.1 christos reactivate_node(rbtdb, node, rbtdbiter->tree_locked);
8884 1.1 christos }
8885 1.1 christos
8886 1.1 christos static inline void
8887 1.1 christos dereference_iter_node(rbtdb_dbiterator_t *rbtdbiter) {
8888 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db;
8889 1.1 christos dns_rbtnode_t *node = rbtdbiter->node;
8890 1.1 christos nodelock_t *lock;
8891 1.1 christos
8892 1.1 christos if (node == NULL)
8893 1.1 christos return;
8894 1.1 christos
8895 1.1 christos lock = &rbtdb->node_locks[node->locknum].lock;
8896 1.1 christos NODE_LOCK(lock, isc_rwlocktype_read);
8897 1.1 christos decrement_reference(rbtdb, node, 0, isc_rwlocktype_read,
8898 1.3 christos rbtdbiter->tree_locked, false);
8899 1.1 christos NODE_UNLOCK(lock, isc_rwlocktype_read);
8900 1.1 christos
8901 1.1 christos rbtdbiter->node = NULL;
8902 1.1 christos }
8903 1.1 christos
8904 1.1 christos static void
8905 1.1 christos flush_deletions(rbtdb_dbiterator_t *rbtdbiter) {
8906 1.1 christos dns_rbtnode_t *node;
8907 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db;
8908 1.3 christos bool was_read_locked = false;
8909 1.1 christos nodelock_t *lock;
8910 1.1 christos int i;
8911 1.1 christos
8912 1.1 christos if (rbtdbiter->delcnt != 0) {
8913 1.1 christos /*
8914 1.1 christos * Note that "%d node of %d in tree" can report things like
8915 1.1 christos * "flush_deletions: 59 nodes of 41 in tree". This means
8916 1.1 christos * That some nodes appear on the deletions list more than
8917 1.1 christos * once. Only the last occurence will actually be deleted.
8918 1.1 christos */
8919 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
8920 1.1 christos DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
8921 1.1 christos "flush_deletions: %d nodes of %d in tree",
8922 1.1 christos rbtdbiter->delcnt,
8923 1.1 christos dns_rbt_nodecount(rbtdb->tree));
8924 1.1 christos
8925 1.1 christos if (rbtdbiter->tree_locked == isc_rwlocktype_read) {
8926 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
8927 1.3 christos was_read_locked = true;
8928 1.1 christos }
8929 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
8930 1.1 christos rbtdbiter->tree_locked = isc_rwlocktype_write;
8931 1.1 christos
8932 1.1 christos for (i = 0; i < rbtdbiter->delcnt; i++) {
8933 1.1 christos node = rbtdbiter->deletions[i];
8934 1.1 christos lock = &rbtdb->node_locks[node->locknum].lock;
8935 1.1 christos
8936 1.1 christos NODE_LOCK(lock, isc_rwlocktype_read);
8937 1.1 christos decrement_reference(rbtdb, node, 0,
8938 1.1 christos isc_rwlocktype_read,
8939 1.3 christos rbtdbiter->tree_locked, false);
8940 1.1 christos NODE_UNLOCK(lock, isc_rwlocktype_read);
8941 1.1 christos }
8942 1.1 christos
8943 1.1 christos rbtdbiter->delcnt = 0;
8944 1.1 christos
8945 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
8946 1.1 christos if (was_read_locked) {
8947 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
8948 1.1 christos rbtdbiter->tree_locked = isc_rwlocktype_read;
8949 1.1 christos
8950 1.1 christos } else {
8951 1.1 christos rbtdbiter->tree_locked = isc_rwlocktype_none;
8952 1.1 christos }
8953 1.1 christos }
8954 1.1 christos }
8955 1.1 christos
8956 1.1 christos static inline void
8957 1.1 christos resume_iteration(rbtdb_dbiterator_t *rbtdbiter) {
8958 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db;
8959 1.1 christos
8960 1.1 christos REQUIRE(rbtdbiter->paused);
8961 1.1 christos REQUIRE(rbtdbiter->tree_locked == isc_rwlocktype_none);
8962 1.1 christos
8963 1.1 christos RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
8964 1.1 christos rbtdbiter->tree_locked = isc_rwlocktype_read;
8965 1.1 christos
8966 1.3 christos rbtdbiter->paused = false;
8967 1.1 christos }
8968 1.1 christos
8969 1.1 christos static void
8970 1.1 christos dbiterator_destroy(dns_dbiterator_t **iteratorp) {
8971 1.1 christos rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)(*iteratorp);
8972 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db;
8973 1.1 christos dns_db_t *db = NULL;
8974 1.1 christos
8975 1.1 christos if (rbtdbiter->tree_locked == isc_rwlocktype_read) {
8976 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
8977 1.1 christos rbtdbiter->tree_locked = isc_rwlocktype_none;
8978 1.1 christos } else
8979 1.1 christos INSIST(rbtdbiter->tree_locked == isc_rwlocktype_none);
8980 1.1 christos
8981 1.1 christos dereference_iter_node(rbtdbiter);
8982 1.1 christos
8983 1.1 christos flush_deletions(rbtdbiter);
8984 1.1 christos
8985 1.1 christos dns_db_attach(rbtdbiter->common.db, &db);
8986 1.1 christos dns_db_detach(&rbtdbiter->common.db);
8987 1.1 christos
8988 1.1 christos dns_rbtnodechain_reset(&rbtdbiter->chain);
8989 1.1 christos dns_rbtnodechain_reset(&rbtdbiter->nsec3chain);
8990 1.1 christos isc_mem_put(db->mctx, rbtdbiter, sizeof(*rbtdbiter));
8991 1.1 christos dns_db_detach(&db);
8992 1.1 christos
8993 1.1 christos *iteratorp = NULL;
8994 1.1 christos }
8995 1.1 christos
8996 1.1 christos static isc_result_t
8997 1.1 christos dbiterator_first(dns_dbiterator_t *iterator) {
8998 1.1 christos isc_result_t result;
8999 1.1 christos rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
9000 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
9001 1.1 christos dns_name_t *name, *origin;
9002 1.1 christos
9003 1.1 christos if (rbtdbiter->result != ISC_R_SUCCESS &&
9004 1.1 christos rbtdbiter->result != ISC_R_NOTFOUND &&
9005 1.1 christos rbtdbiter->result != DNS_R_PARTIALMATCH &&
9006 1.1 christos rbtdbiter->result != ISC_R_NOMORE)
9007 1.1 christos return (rbtdbiter->result);
9008 1.1 christos
9009 1.1 christos if (rbtdbiter->paused)
9010 1.1 christos resume_iteration(rbtdbiter);
9011 1.1 christos
9012 1.1 christos dereference_iter_node(rbtdbiter);
9013 1.1 christos
9014 1.1 christos name = dns_fixedname_name(&rbtdbiter->name);
9015 1.1 christos origin = dns_fixedname_name(&rbtdbiter->origin);
9016 1.1 christos dns_rbtnodechain_reset(&rbtdbiter->chain);
9017 1.1 christos dns_rbtnodechain_reset(&rbtdbiter->nsec3chain);
9018 1.1 christos
9019 1.1 christos if (rbtdbiter->nsec3only) {
9020 1.1 christos rbtdbiter->current = &rbtdbiter->nsec3chain;
9021 1.1 christos result = dns_rbtnodechain_first(rbtdbiter->current,
9022 1.1 christos rbtdb->nsec3, name, origin);
9023 1.1 christos } else {
9024 1.1 christos rbtdbiter->current = &rbtdbiter->chain;
9025 1.1 christos result = dns_rbtnodechain_first(rbtdbiter->current,
9026 1.1 christos rbtdb->tree, name, origin);
9027 1.1 christos if (!rbtdbiter->nonsec3 && result == ISC_R_NOTFOUND) {
9028 1.1 christos rbtdbiter->current = &rbtdbiter->nsec3chain;
9029 1.1 christos result = dns_rbtnodechain_first(rbtdbiter->current,
9030 1.1 christos rbtdb->nsec3, name,
9031 1.1 christos origin);
9032 1.1 christos }
9033 1.1 christos }
9034 1.1 christos if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
9035 1.1 christos result = dns_rbtnodechain_current(rbtdbiter->current, NULL,
9036 1.1 christos NULL, &rbtdbiter->node);
9037 1.1 christos if (result == ISC_R_SUCCESS) {
9038 1.3 christos rbtdbiter->new_origin = true;
9039 1.1 christos reference_iter_node(rbtdbiter);
9040 1.1 christos }
9041 1.1 christos } else {
9042 1.1 christos INSIST(result == ISC_R_NOTFOUND);
9043 1.1 christos result = ISC_R_NOMORE; /* The tree is empty. */
9044 1.1 christos }
9045 1.1 christos
9046 1.1 christos rbtdbiter->result = result;
9047 1.1 christos
9048 1.1 christos if (result != ISC_R_SUCCESS)
9049 1.1 christos ENSURE(!rbtdbiter->paused);
9050 1.1 christos
9051 1.1 christos return (result);
9052 1.1 christos }
9053 1.1 christos
9054 1.1 christos static isc_result_t
9055 1.1 christos dbiterator_last(dns_dbiterator_t *iterator) {
9056 1.1 christos isc_result_t result;
9057 1.1 christos rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
9058 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
9059 1.1 christos dns_name_t *name, *origin;
9060 1.1 christos
9061 1.1 christos if (rbtdbiter->result != ISC_R_SUCCESS &&
9062 1.1 christos rbtdbiter->result != ISC_R_NOTFOUND &&
9063 1.1 christos rbtdbiter->result != DNS_R_PARTIALMATCH &&
9064 1.1 christos rbtdbiter->result != ISC_R_NOMORE)
9065 1.1 christos return (rbtdbiter->result);
9066 1.1 christos
9067 1.1 christos if (rbtdbiter->paused)
9068 1.1 christos resume_iteration(rbtdbiter);
9069 1.1 christos
9070 1.1 christos dereference_iter_node(rbtdbiter);
9071 1.1 christos
9072 1.1 christos name = dns_fixedname_name(&rbtdbiter->name);
9073 1.1 christos origin = dns_fixedname_name(&rbtdbiter->origin);
9074 1.1 christos dns_rbtnodechain_reset(&rbtdbiter->chain);
9075 1.1 christos dns_rbtnodechain_reset(&rbtdbiter->nsec3chain);
9076 1.1 christos
9077 1.1 christos result = ISC_R_NOTFOUND;
9078 1.1 christos if (rbtdbiter->nsec3only && !rbtdbiter->nonsec3) {
9079 1.1 christos rbtdbiter->current = &rbtdbiter->nsec3chain;
9080 1.1 christos result = dns_rbtnodechain_last(rbtdbiter->current,
9081 1.1 christos rbtdb->nsec3, name, origin);
9082 1.1 christos }
9083 1.1 christos if (!rbtdbiter->nsec3only && result == ISC_R_NOTFOUND) {
9084 1.1 christos rbtdbiter->current = &rbtdbiter->chain;
9085 1.1 christos result = dns_rbtnodechain_last(rbtdbiter->current, rbtdb->tree,
9086 1.1 christos name, origin);
9087 1.1 christos }
9088 1.1 christos if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
9089 1.1 christos result = dns_rbtnodechain_current(rbtdbiter->current, NULL,
9090 1.1 christos NULL, &rbtdbiter->node);
9091 1.1 christos if (result == ISC_R_SUCCESS) {
9092 1.3 christos rbtdbiter->new_origin = true;
9093 1.1 christos reference_iter_node(rbtdbiter);
9094 1.1 christos }
9095 1.1 christos } else {
9096 1.1 christos INSIST(result == ISC_R_NOTFOUND);
9097 1.1 christos result = ISC_R_NOMORE; /* The tree is empty. */
9098 1.1 christos }
9099 1.1 christos
9100 1.1 christos rbtdbiter->result = result;
9101 1.1 christos
9102 1.1 christos return (result);
9103 1.1 christos }
9104 1.1 christos
9105 1.1 christos static isc_result_t
9106 1.1 christos dbiterator_seek(dns_dbiterator_t *iterator, const dns_name_t *name) {
9107 1.1 christos isc_result_t result, tresult;
9108 1.1 christos rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
9109 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
9110 1.1 christos dns_name_t *iname, *origin;
9111 1.1 christos
9112 1.1 christos if (rbtdbiter->result != ISC_R_SUCCESS &&
9113 1.1 christos rbtdbiter->result != ISC_R_NOTFOUND &&
9114 1.1 christos rbtdbiter->result != DNS_R_PARTIALMATCH &&
9115 1.1 christos rbtdbiter->result != ISC_R_NOMORE)
9116 1.1 christos return (rbtdbiter->result);
9117 1.1 christos
9118 1.1 christos if (rbtdbiter->paused)
9119 1.1 christos resume_iteration(rbtdbiter);
9120 1.1 christos
9121 1.1 christos dereference_iter_node(rbtdbiter);
9122 1.1 christos
9123 1.1 christos iname = dns_fixedname_name(&rbtdbiter->name);
9124 1.1 christos origin = dns_fixedname_name(&rbtdbiter->origin);
9125 1.1 christos dns_rbtnodechain_reset(&rbtdbiter->chain);
9126 1.1 christos dns_rbtnodechain_reset(&rbtdbiter->nsec3chain);
9127 1.1 christos
9128 1.1 christos if (rbtdbiter->nsec3only) {
9129 1.1 christos rbtdbiter->current = &rbtdbiter->nsec3chain;
9130 1.1 christos result = dns_rbt_findnode(rbtdb->nsec3, name, NULL,
9131 1.1 christos &rbtdbiter->node,
9132 1.1 christos rbtdbiter->current,
9133 1.1 christos DNS_RBTFIND_EMPTYDATA, NULL, NULL);
9134 1.1 christos } else if (rbtdbiter->nonsec3) {
9135 1.1 christos rbtdbiter->current = &rbtdbiter->chain;
9136 1.1 christos result = dns_rbt_findnode(rbtdb->tree, name, NULL,
9137 1.1 christos &rbtdbiter->node,
9138 1.1 christos rbtdbiter->current,
9139 1.1 christos DNS_RBTFIND_EMPTYDATA, NULL, NULL);
9140 1.1 christos } else {
9141 1.1 christos /*
9142 1.1 christos * Stay on main chain if not found on either chain.
9143 1.1 christos */
9144 1.1 christos rbtdbiter->current = &rbtdbiter->chain;
9145 1.1 christos result = dns_rbt_findnode(rbtdb->tree, name, NULL,
9146 1.1 christos &rbtdbiter->node,
9147 1.1 christos rbtdbiter->current,
9148 1.1 christos DNS_RBTFIND_EMPTYDATA, NULL, NULL);
9149 1.1 christos if (result == DNS_R_PARTIALMATCH) {
9150 1.1 christos dns_rbtnode_t *node = NULL;
9151 1.1 christos tresult = dns_rbt_findnode(rbtdb->nsec3, name, NULL,
9152 1.1 christos &node, &rbtdbiter->nsec3chain,
9153 1.1 christos DNS_RBTFIND_EMPTYDATA,
9154 1.1 christos NULL, NULL);
9155 1.1 christos if (tresult == ISC_R_SUCCESS) {
9156 1.1 christos rbtdbiter->node = node;
9157 1.1 christos rbtdbiter->current = &rbtdbiter->nsec3chain;
9158 1.1 christos result = tresult;
9159 1.1 christos }
9160 1.1 christos }
9161 1.1 christos }
9162 1.1 christos
9163 1.1 christos if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
9164 1.1 christos tresult = dns_rbtnodechain_current(rbtdbiter->current, iname,
9165 1.1 christos origin, NULL);
9166 1.1 christos if (tresult == ISC_R_SUCCESS) {
9167 1.3 christos rbtdbiter->new_origin = true;
9168 1.1 christos reference_iter_node(rbtdbiter);
9169 1.1 christos } else {
9170 1.1 christos result = tresult;
9171 1.1 christos rbtdbiter->node = NULL;
9172 1.1 christos }
9173 1.1 christos } else
9174 1.1 christos rbtdbiter->node = NULL;
9175 1.1 christos
9176 1.1 christos rbtdbiter->result = (result == DNS_R_PARTIALMATCH) ?
9177 1.1 christos ISC_R_SUCCESS : result;
9178 1.1 christos
9179 1.1 christos return (result);
9180 1.1 christos }
9181 1.1 christos
9182 1.1 christos static isc_result_t
9183 1.1 christos dbiterator_prev(dns_dbiterator_t *iterator) {
9184 1.1 christos isc_result_t result;
9185 1.1 christos rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
9186 1.1 christos dns_name_t *name, *origin;
9187 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
9188 1.1 christos
9189 1.1 christos REQUIRE(rbtdbiter->node != NULL);
9190 1.1 christos
9191 1.1 christos if (rbtdbiter->result != ISC_R_SUCCESS)
9192 1.1 christos return (rbtdbiter->result);
9193 1.1 christos
9194 1.1 christos if (rbtdbiter->paused)
9195 1.1 christos resume_iteration(rbtdbiter);
9196 1.1 christos
9197 1.1 christos name = dns_fixedname_name(&rbtdbiter->name);
9198 1.1 christos origin = dns_fixedname_name(&rbtdbiter->origin);
9199 1.1 christos result = dns_rbtnodechain_prev(rbtdbiter->current, name, origin);
9200 1.1 christos if (result == ISC_R_NOMORE && !rbtdbiter->nsec3only &&
9201 1.1 christos !rbtdbiter->nonsec3 &&
9202 1.1 christos &rbtdbiter->nsec3chain == rbtdbiter->current) {
9203 1.1 christos rbtdbiter->current = &rbtdbiter->chain;
9204 1.1 christos dns_rbtnodechain_reset(rbtdbiter->current);
9205 1.1 christos result = dns_rbtnodechain_last(rbtdbiter->current, rbtdb->tree,
9206 1.1 christos name, origin);
9207 1.1 christos if (result == ISC_R_NOTFOUND)
9208 1.1 christos result = ISC_R_NOMORE;
9209 1.1 christos }
9210 1.1 christos
9211 1.1 christos dereference_iter_node(rbtdbiter);
9212 1.1 christos
9213 1.1 christos if (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
9214 1.3 christos rbtdbiter->new_origin = (result == DNS_R_NEWORIGIN);
9215 1.1 christos result = dns_rbtnodechain_current(rbtdbiter->current, NULL,
9216 1.1 christos NULL, &rbtdbiter->node);
9217 1.1 christos }
9218 1.1 christos
9219 1.1 christos if (result == ISC_R_SUCCESS)
9220 1.1 christos reference_iter_node(rbtdbiter);
9221 1.1 christos
9222 1.1 christos rbtdbiter->result = result;
9223 1.1 christos
9224 1.1 christos return (result);
9225 1.1 christos }
9226 1.1 christos
9227 1.1 christos static isc_result_t
9228 1.1 christos dbiterator_next(dns_dbiterator_t *iterator) {
9229 1.1 christos isc_result_t result;
9230 1.1 christos rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
9231 1.1 christos dns_name_t *name, *origin;
9232 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
9233 1.1 christos
9234 1.1 christos REQUIRE(rbtdbiter->node != NULL);
9235 1.1 christos
9236 1.1 christos if (rbtdbiter->result != ISC_R_SUCCESS)
9237 1.1 christos return (rbtdbiter->result);
9238 1.1 christos
9239 1.1 christos if (rbtdbiter->paused)
9240 1.1 christos resume_iteration(rbtdbiter);
9241 1.1 christos
9242 1.1 christos name = dns_fixedname_name(&rbtdbiter->name);
9243 1.1 christos origin = dns_fixedname_name(&rbtdbiter->origin);
9244 1.1 christos result = dns_rbtnodechain_next(rbtdbiter->current, name, origin);
9245 1.1 christos if (result == ISC_R_NOMORE && !rbtdbiter->nsec3only &&
9246 1.1 christos !rbtdbiter->nonsec3 && &rbtdbiter->chain == rbtdbiter->current) {
9247 1.1 christos rbtdbiter->current = &rbtdbiter->nsec3chain;
9248 1.1 christos dns_rbtnodechain_reset(rbtdbiter->current);
9249 1.1 christos result = dns_rbtnodechain_first(rbtdbiter->current,
9250 1.1 christos rbtdb->nsec3, name, origin);
9251 1.1 christos if (result == ISC_R_NOTFOUND)
9252 1.1 christos result = ISC_R_NOMORE;
9253 1.1 christos }
9254 1.1 christos
9255 1.1 christos dereference_iter_node(rbtdbiter);
9256 1.1 christos
9257 1.1 christos if (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
9258 1.3 christos rbtdbiter->new_origin = (result == DNS_R_NEWORIGIN);
9259 1.1 christos result = dns_rbtnodechain_current(rbtdbiter->current, NULL,
9260 1.1 christos NULL, &rbtdbiter->node);
9261 1.1 christos }
9262 1.1 christos if (result == ISC_R_SUCCESS)
9263 1.1 christos reference_iter_node(rbtdbiter);
9264 1.1 christos
9265 1.1 christos rbtdbiter->result = result;
9266 1.1 christos
9267 1.1 christos return (result);
9268 1.1 christos }
9269 1.1 christos
9270 1.1 christos static isc_result_t
9271 1.1 christos dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep,
9272 1.1 christos dns_name_t *name)
9273 1.1 christos {
9274 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
9275 1.1 christos rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
9276 1.1 christos dns_rbtnode_t *node = rbtdbiter->node;
9277 1.1 christos isc_result_t result;
9278 1.1 christos dns_name_t *nodename = dns_fixedname_name(&rbtdbiter->name);
9279 1.1 christos dns_name_t *origin = dns_fixedname_name(&rbtdbiter->origin);
9280 1.1 christos
9281 1.1 christos REQUIRE(rbtdbiter->result == ISC_R_SUCCESS);
9282 1.1 christos REQUIRE(rbtdbiter->node != NULL);
9283 1.1 christos
9284 1.1 christos if (rbtdbiter->paused)
9285 1.1 christos resume_iteration(rbtdbiter);
9286 1.1 christos
9287 1.1 christos if (name != NULL) {
9288 1.1 christos if (rbtdbiter->common.relative_names)
9289 1.1 christos origin = NULL;
9290 1.1 christos result = dns_name_concatenate(nodename, origin, name, NULL);
9291 1.1 christos if (result != ISC_R_SUCCESS)
9292 1.1 christos return (result);
9293 1.1 christos if (rbtdbiter->common.relative_names && rbtdbiter->new_origin)
9294 1.1 christos result = DNS_R_NEWORIGIN;
9295 1.1 christos } else
9296 1.1 christos result = ISC_R_SUCCESS;
9297 1.1 christos
9298 1.1 christos new_reference(rbtdb, node);
9299 1.1 christos
9300 1.1 christos *nodep = rbtdbiter->node;
9301 1.1 christos
9302 1.1 christos if (iterator->cleaning && result == ISC_R_SUCCESS) {
9303 1.1 christos isc_result_t expire_result;
9304 1.1 christos
9305 1.1 christos /*
9306 1.1 christos * If the deletion array is full, flush it before trying
9307 1.1 christos * to expire the current node. The current node can't
9308 1.1 christos * fully deleted while the iteration cursor is still on it.
9309 1.1 christos */
9310 1.1 christos if (rbtdbiter->delcnt == DELETION_BATCH_MAX)
9311 1.1 christos flush_deletions(rbtdbiter);
9312 1.1 christos
9313 1.1 christos expire_result = expirenode(iterator->db, *nodep, 0);
9314 1.1 christos
9315 1.1 christos /*
9316 1.1 christos * expirenode() currently always returns success.
9317 1.1 christos */
9318 1.1 christos if (expire_result == ISC_R_SUCCESS && node->down == NULL) {
9319 1.1 christos rbtdbiter->deletions[rbtdbiter->delcnt++] = node;
9320 1.3 christos isc_refcount_increment(&node->references);
9321 1.1 christos }
9322 1.1 christos }
9323 1.1 christos
9324 1.1 christos return (result);
9325 1.1 christos }
9326 1.1 christos
9327 1.1 christos static isc_result_t
9328 1.1 christos dbiterator_pause(dns_dbiterator_t *iterator) {
9329 1.1 christos dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
9330 1.1 christos rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
9331 1.1 christos
9332 1.1 christos if (rbtdbiter->result != ISC_R_SUCCESS &&
9333 1.1 christos rbtdbiter->result != ISC_R_NOTFOUND &&
9334 1.1 christos rbtdbiter->result != DNS_R_PARTIALMATCH &&
9335 1.1 christos rbtdbiter->result != ISC_R_NOMORE)
9336 1.1 christos return (rbtdbiter->result);
9337 1.1 christos
9338 1.1 christos if (rbtdbiter->paused)
9339 1.1 christos return (ISC_R_SUCCESS);
9340 1.1 christos
9341 1.3 christos rbtdbiter->paused = true;
9342 1.1 christos
9343 1.1 christos if (rbtdbiter->tree_locked != isc_rwlocktype_none) {
9344 1.1 christos INSIST(rbtdbiter->tree_locked == isc_rwlocktype_read);
9345 1.1 christos RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
9346 1.1 christos rbtdbiter->tree_locked = isc_rwlocktype_none;
9347 1.1 christos }
9348 1.1 christos
9349 1.1 christos flush_deletions(rbtdbiter);
9350 1.1 christos
9351 1.1 christos return (ISC_R_SUCCESS);
9352 1.1 christos }
9353 1.1 christos
9354 1.1 christos static isc_result_t
9355 1.1 christos dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) {
9356 1.1 christos rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
9357 1.1 christos dns_name_t *origin = dns_fixedname_name(&rbtdbiter->origin);
9358 1.1 christos
9359 1.1 christos if (rbtdbiter->result != ISC_R_SUCCESS)
9360 1.1 christos return (rbtdbiter->result);
9361 1.1 christos
9362 1.1 christos return (dns_name_copy(origin, name, NULL));
9363 1.1 christos }
9364 1.1 christos
9365 1.1 christos static void
9366 1.1 christos setownercase(rdatasetheader_t *header, const dns_name_t *name) {
9367 1.1 christos unsigned int i;
9368 1.3 christos bool fully_lower;
9369 1.1 christos
9370 1.1 christos /*
9371 1.1 christos * We do not need to worry about label lengths as they are all
9372 1.1 christos * less than or equal to 63.
9373 1.1 christos */
9374 1.1 christos memset(header->upper, 0, sizeof(header->upper));
9375 1.3 christos fully_lower = true;
9376 1.1 christos for (i = 0; i < name->length; i++)
9377 1.1 christos if (name->ndata[i] >= 0x41 && name->ndata[i] <= 0x5a) {
9378 1.1 christos header->upper[i/8] |= 1 << (i%8);
9379 1.3 christos fully_lower = false;
9380 1.1 christos }
9381 1.1 christos header->attributes |= RDATASET_ATTR_CASESET;
9382 1.1 christos if (ISC_LIKELY(fully_lower))
9383 1.1 christos header->attributes |= RDATASET_ATTR_CASEFULLYLOWER;
9384 1.1 christos }
9385 1.1 christos
9386 1.1 christos static void
9387 1.1 christos rdataset_setownercase(dns_rdataset_t *rdataset, const dns_name_t *name) {
9388 1.1 christos unsigned char *raw = rdataset->private3; /* RDATASLAB */
9389 1.1 christos rdatasetheader_t *header;
9390 1.1 christos
9391 1.1 christos header = (struct rdatasetheader *)(raw - sizeof(*header));
9392 1.1 christos setownercase(header, name);
9393 1.1 christos }
9394 1.1 christos
9395 1.1 christos static const unsigned char charmask[] = {
9396 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9397 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9398 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9399 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9400 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9401 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9402 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9403 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9404 1.1 christos 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
9405 1.1 christos 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
9406 1.1 christos 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
9407 1.1 christos 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
9408 1.1 christos 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
9409 1.1 christos 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
9410 1.1 christos 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
9411 1.1 christos 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
9412 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9413 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9414 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9415 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9416 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9417 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9418 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9419 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9420 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9421 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9422 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9423 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9424 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9425 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9426 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9427 1.1 christos 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
9428 1.1 christos };
9429 1.1 christos
9430 1.1 christos static const unsigned char maptolower[] = {
9431 1.1 christos 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
9432 1.1 christos 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
9433 1.1 christos 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
9434 1.1 christos 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
9435 1.1 christos 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
9436 1.1 christos 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
9437 1.1 christos 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
9438 1.1 christos 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
9439 1.1 christos 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
9440 1.1 christos 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
9441 1.1 christos 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
9442 1.1 christos 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
9443 1.1 christos 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
9444 1.1 christos 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
9445 1.1 christos 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
9446 1.1 christos 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
9447 1.1 christos 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
9448 1.1 christos 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
9449 1.1 christos 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
9450 1.1 christos 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
9451 1.1 christos 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
9452 1.1 christos 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
9453 1.1 christos 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
9454 1.1 christos 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
9455 1.1 christos 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
9456 1.1 christos 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
9457 1.1 christos 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
9458 1.1 christos 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
9459 1.1 christos 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
9460 1.1 christos 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
9461 1.1 christos 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
9462 1.1 christos 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
9463 1.1 christos };
9464 1.1 christos
9465 1.1 christos static void
9466 1.1 christos rdataset_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name) {
9467 1.1 christos const unsigned char *raw = rdataset->private3; /* RDATASLAB */
9468 1.1 christos const rdatasetheader_t *header;
9469 1.1 christos unsigned int i, j;
9470 1.1 christos unsigned char bits;
9471 1.1 christos unsigned char c, flip;
9472 1.1 christos
9473 1.1 christos header = (const struct rdatasetheader *)(raw - sizeof(*header));
9474 1.1 christos
9475 1.1 christos if (!CASESET(header))
9476 1.1 christos return;
9477 1.1 christos
9478 1.1 christos #if 0
9479 1.1 christos /*
9480 1.1 christos * This was the original code, and is implemented differently in
9481 1.1 christos * the #else block that follows.
9482 1.1 christos */
9483 1.1 christos for (i = 0; i < name->length; i++) {
9484 1.1 christos /*
9485 1.1 christos * Set the case bit if it does not match the recorded bit.
9486 1.1 christos */
9487 1.1 christos if (name->ndata[i] >= 0x61 && name->ndata[i] <= 0x7a &&
9488 1.1 christos (header->upper[i/8] & (1 << (i%8))) != 0)
9489 1.1 christos name->ndata[i] &= ~0x20; /* clear the lower case bit */
9490 1.1 christos else if (name->ndata[i] >= 0x41 && name->ndata[i] <= 0x5a &&
9491 1.1 christos (header->upper[i/8] & (1 << (i%8))) == 0)
9492 1.1 christos name->ndata[i] |= 0x20; /* set the lower case bit */
9493 1.1 christos }
9494 1.1 christos #else
9495 1.1 christos
9496 1.1 christos if (ISC_LIKELY(CASEFULLYLOWER(header))) {
9497 1.1 christos unsigned char *bp, *be;
9498 1.1 christos bp = name->ndata;
9499 1.1 christos be = bp + name->length;
9500 1.1 christos
9501 1.1 christos while (bp <= be - 4) {
9502 1.1 christos c = bp[0];
9503 1.1 christos bp[0] = maptolower[c];
9504 1.1 christos c = bp[1];
9505 1.1 christos bp[1] = maptolower[c];
9506 1.1 christos c = bp[2];
9507 1.1 christos bp[2] = maptolower[c];
9508 1.1 christos c = bp[3];
9509 1.1 christos bp[3] = maptolower[c];
9510 1.1 christos bp += 4;
9511 1.1 christos }
9512 1.1 christos while (bp < be) {
9513 1.1 christos c = *bp;
9514 1.1 christos *bp++ = maptolower[c];
9515 1.1 christos }
9516 1.1 christos return;
9517 1.1 christos }
9518 1.1 christos
9519 1.1 christos i = 0;
9520 1.1 christos for (j = 0; j < (name->length >> 3); j++) {
9521 1.1 christos unsigned int k;
9522 1.1 christos
9523 1.1 christos bits = ~(header->upper[j]);
9524 1.1 christos
9525 1.1 christos for (k = 0; k < 8; k++) {
9526 1.1 christos c = name->ndata[i];
9527 1.1 christos flip = (bits & 1) << 5;
9528 1.1 christos flip ^= c;
9529 1.1 christos flip &= charmask[c];
9530 1.1 christos name->ndata[i] ^= flip;
9531 1.1 christos
9532 1.1 christos i++;
9533 1.1 christos bits >>= 1;
9534 1.1 christos }
9535 1.1 christos }
9536 1.1 christos
9537 1.1 christos if (ISC_UNLIKELY(i == name->length))
9538 1.1 christos return;
9539 1.1 christos
9540 1.1 christos bits = ~(header->upper[j]);
9541 1.1 christos
9542 1.1 christos for (; i < name->length; i++) {
9543 1.1 christos c = name->ndata[i];
9544 1.1 christos flip = (bits & 1) << 5;
9545 1.1 christos flip ^= c;
9546 1.1 christos flip &= charmask[c];
9547 1.1 christos name->ndata[i] ^= flip;
9548 1.1 christos
9549 1.1 christos bits >>= 1;
9550 1.1 christos }
9551 1.1 christos #endif
9552 1.1 christos }
9553 1.1 christos
9554 1.1 christos struct rbtdb_glue {
9555 1.1 christos struct rbtdb_glue *next;
9556 1.1 christos dns_fixedname_t fixedname;
9557 1.1 christos dns_rdataset_t rdataset_a;
9558 1.1 christos dns_rdataset_t sigrdataset_a;
9559 1.1 christos dns_rdataset_t rdataset_aaaa;
9560 1.1 christos dns_rdataset_t sigrdataset_aaaa;
9561 1.1 christos };
9562 1.1 christos
9563 1.1 christos typedef struct {
9564 1.1 christos rbtdb_glue_t *glue_list;
9565 1.1 christos dns_rbtdb_t *rbtdb;
9566 1.1 christos rbtdb_version_t *rbtversion;
9567 1.1 christos } rbtdb_glue_additionaldata_ctx_t;
9568 1.1 christos
9569 1.1 christos static void
9570 1.1 christos free_gluelist(rbtdb_glue_t *glue_list, dns_rbtdb_t *rbtdb) {
9571 1.1 christos rbtdb_glue_t *cur, *cur_next;
9572 1.1 christos
9573 1.1 christos if (glue_list == (void *) -1)
9574 1.1 christos return;
9575 1.1 christos
9576 1.1 christos cur = glue_list;
9577 1.1 christos while (cur != NULL) {
9578 1.1 christos cur_next = cur->next;
9579 1.1 christos
9580 1.1 christos if (dns_rdataset_isassociated(&cur->rdataset_a))
9581 1.1 christos dns_rdataset_disassociate(&cur->rdataset_a);
9582 1.1 christos if (dns_rdataset_isassociated(&cur->sigrdataset_a))
9583 1.1 christos dns_rdataset_disassociate(&cur->sigrdataset_a);
9584 1.1 christos
9585 1.1 christos if (dns_rdataset_isassociated(&cur->rdataset_aaaa))
9586 1.1 christos dns_rdataset_disassociate(&cur->rdataset_aaaa);
9587 1.1 christos if (dns_rdataset_isassociated(&cur->sigrdataset_aaaa))
9588 1.1 christos dns_rdataset_disassociate(&cur->sigrdataset_aaaa);
9589 1.1 christos
9590 1.1 christos dns_rdataset_invalidate(&cur->rdataset_a);
9591 1.1 christos dns_rdataset_invalidate(&cur->sigrdataset_a);
9592 1.1 christos dns_rdataset_invalidate(&cur->rdataset_aaaa);
9593 1.1 christos dns_rdataset_invalidate(&cur->sigrdataset_aaaa);
9594 1.1 christos
9595 1.1 christos isc_mem_put(rbtdb->common.mctx, cur, sizeof(*cur));
9596 1.1 christos cur = cur_next;
9597 1.1 christos }
9598 1.1 christos }
9599 1.1 christos
9600 1.1 christos static void
9601 1.1 christos free_gluetable(rbtdb_version_t *version) {
9602 1.1 christos dns_rbtdb_t *rbtdb;
9603 1.1 christos size_t i;
9604 1.1 christos
9605 1.1 christos RWLOCK(&version->glue_rwlock, isc_rwlocktype_write);
9606 1.1 christos
9607 1.1 christos rbtdb = version->rbtdb;
9608 1.1 christos
9609 1.1 christos for (i = 0; i < version->glue_table_size; i++) {
9610 1.1 christos rbtdb_glue_table_node_t *cur, *cur_next;
9611 1.1 christos
9612 1.1 christos cur = version->glue_table[i];
9613 1.1 christos while (cur != NULL) {
9614 1.1 christos cur_next = cur->next;
9615 1.3 christos /* isc_refcount_decrement(&cur->node->references); */
9616 1.1 christos cur->node = NULL;
9617 1.1 christos free_gluelist(cur->glue_list, rbtdb);
9618 1.1 christos cur->glue_list = NULL;
9619 1.1 christos isc_mem_put(rbtdb->common.mctx, cur, sizeof(*cur));
9620 1.1 christos cur = cur_next;
9621 1.1 christos }
9622 1.1 christos version->glue_table[i] = NULL;
9623 1.1 christos }
9624 1.1 christos
9625 1.1 christos isc_mem_put(rbtdb->common.mctx, version->glue_table,
9626 1.1 christos (sizeof(*version->glue_table) *
9627 1.1 christos version->glue_table_size));
9628 1.1 christos
9629 1.1 christos RWUNLOCK(&version->glue_rwlock, isc_rwlocktype_write);
9630 1.1 christos }
9631 1.1 christos
9632 1.3 christos static bool
9633 1.1 christos rehash_gluetable(rbtdb_version_t *version) {
9634 1.1 christos size_t oldsize, i;
9635 1.1 christos rbtdb_glue_table_node_t **oldtable;
9636 1.1 christos rbtdb_glue_table_node_t *gluenode;
9637 1.1 christos rbtdb_glue_table_node_t *nextgluenode;
9638 1.3 christos uint32_t hash;
9639 1.1 christos
9640 1.1 christos if (ISC_LIKELY(version->glue_table_nodecount <
9641 1.1 christos (version->glue_table_size * 3U)))
9642 1.3 christos return (false);
9643 1.1 christos
9644 1.1 christos oldsize = version->glue_table_size;
9645 1.1 christos oldtable = version->glue_table;
9646 1.1 christos do {
9647 1.1 christos INSIST((version->glue_table_size * 2 + 1) >
9648 1.1 christos version->glue_table_size);
9649 1.1 christos version->glue_table_size = version->glue_table_size * 2 + 1;
9650 1.1 christos } while (version->glue_table_nodecount >=
9651 1.1 christos (version->glue_table_size * 3U));
9652 1.1 christos
9653 1.1 christos version->glue_table = (rbtdb_glue_table_node_t **)
9654 1.1 christos isc_mem_get(version->rbtdb->common.mctx,
9655 1.1 christos (version->glue_table_size *
9656 1.1 christos sizeof(*version->glue_table)));
9657 1.1 christos if (ISC_UNLIKELY(version->glue_table == NULL)) {
9658 1.1 christos version->glue_table = oldtable;
9659 1.1 christos version->glue_table_size = oldsize;
9660 1.3 christos return (false);
9661 1.1 christos }
9662 1.1 christos
9663 1.1 christos for (i = 0; i < version->glue_table_size; i++)
9664 1.1 christos version->glue_table[i] = NULL;
9665 1.1 christos
9666 1.1 christos for (i = 0; i < oldsize; i++) {
9667 1.1 christos for (gluenode = oldtable[i];
9668 1.1 christos gluenode != NULL;
9669 1.1 christos gluenode = nextgluenode)
9670 1.1 christos {
9671 1.1 christos hash = isc_hash_function(&gluenode->node,
9672 1.1 christos sizeof(gluenode->node),
9673 1.3 christos true, NULL) %
9674 1.1 christos version->glue_table_size;
9675 1.1 christos nextgluenode = gluenode->next;
9676 1.1 christos gluenode->next = version->glue_table[hash];
9677 1.1 christos version->glue_table[hash] = gluenode;
9678 1.1 christos }
9679 1.1 christos }
9680 1.1 christos
9681 1.1 christos isc_mem_put(version->rbtdb->common.mctx, oldtable,
9682 1.1 christos oldsize * sizeof(*version->glue_table));
9683 1.1 christos
9684 1.1 christos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
9685 1.1 christos DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(3),
9686 1.1 christos "rehash_gluetable(): "
9687 1.3 christos "resized glue table from %" PRIu64 " to "
9688 1.3 christos "%" PRIu64,
9689 1.3 christos (uint64_t) oldsize,
9690 1.3 christos (uint64_t) version->glue_table_size);
9691 1.1 christos
9692 1.3 christos return (true);
9693 1.1 christos }
9694 1.1 christos
9695 1.1 christos static isc_result_t
9696 1.1 christos glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype) {
9697 1.1 christos rbtdb_glue_additionaldata_ctx_t *ctx;
9698 1.1 christos isc_result_t result;
9699 1.1 christos dns_fixedname_t fixedname_a;
9700 1.1 christos dns_name_t *name_a = NULL;
9701 1.1 christos dns_rdataset_t rdataset_a, sigrdataset_a;
9702 1.1 christos dns_rbtnode_t *node_a = NULL;
9703 1.1 christos dns_fixedname_t fixedname_aaaa;
9704 1.1 christos dns_name_t *name_aaaa = NULL;
9705 1.1 christos dns_rdataset_t rdataset_aaaa, sigrdataset_aaaa;
9706 1.1 christos dns_rbtnode_t *node_aaaa = NULL;
9707 1.1 christos rbtdb_glue_t *glue = NULL;
9708 1.1 christos dns_name_t *gluename = NULL;
9709 1.1 christos
9710 1.1 christos /*
9711 1.1 christos * NS records want addresses in additional records.
9712 1.1 christos */
9713 1.1 christos INSIST(qtype == dns_rdatatype_a);
9714 1.1 christos
9715 1.1 christos ctx = (rbtdb_glue_additionaldata_ctx_t *) arg;
9716 1.1 christos
9717 1.1 christos name_a = dns_fixedname_initname(&fixedname_a);
9718 1.1 christos dns_rdataset_init(&rdataset_a);
9719 1.1 christos dns_rdataset_init(&sigrdataset_a);
9720 1.1 christos
9721 1.1 christos name_aaaa = dns_fixedname_initname(&fixedname_aaaa);
9722 1.1 christos dns_rdataset_init(&rdataset_aaaa);
9723 1.1 christos dns_rdataset_init(&sigrdataset_aaaa);
9724 1.1 christos
9725 1.1 christos result = zone_find((dns_db_t *) ctx->rbtdb, name, ctx->rbtversion,
9726 1.1 christos dns_rdatatype_a, DNS_DBFIND_GLUEOK, 0,
9727 1.1 christos (dns_dbnode_t **) &node_a, name_a,
9728 1.1 christos &rdataset_a, &sigrdataset_a);
9729 1.1 christos if (result == DNS_R_GLUE) {
9730 1.1 christos glue = isc_mem_get(ctx->rbtdb->common.mctx, sizeof(*glue));
9731 1.1 christos if (glue == NULL) {
9732 1.1 christos result = ISC_R_NOMEMORY;
9733 1.1 christos goto out;
9734 1.1 christos }
9735 1.1 christos
9736 1.1 christos gluename = dns_fixedname_initname(&glue->fixedname);
9737 1.1 christos dns_name_copy(name_a, gluename, NULL);
9738 1.1 christos
9739 1.1 christos dns_rdataset_init(&glue->rdataset_a);
9740 1.1 christos dns_rdataset_init(&glue->sigrdataset_a);
9741 1.1 christos dns_rdataset_init(&glue->rdataset_aaaa);
9742 1.1 christos dns_rdataset_init(&glue->sigrdataset_aaaa);
9743 1.1 christos
9744 1.1 christos dns_rdataset_clone(&rdataset_a, &glue->rdataset_a);
9745 1.1 christos if (dns_rdataset_isassociated(&sigrdataset_a)) {
9746 1.1 christos dns_rdataset_clone(&sigrdataset_a,
9747 1.1 christos &glue->sigrdataset_a);
9748 1.1 christos }
9749 1.1 christos }
9750 1.1 christos
9751 1.1 christos result = zone_find((dns_db_t *) ctx->rbtdb, name, ctx->rbtversion,
9752 1.1 christos dns_rdatatype_aaaa, DNS_DBFIND_GLUEOK, 0,
9753 1.1 christos (dns_dbnode_t **) &node_aaaa, name_aaaa,
9754 1.1 christos &rdataset_aaaa, &sigrdataset_aaaa);
9755 1.1 christos if (result == DNS_R_GLUE) {
9756 1.1 christos if (glue == NULL) {
9757 1.1 christos glue = isc_mem_get(ctx->rbtdb->common.mctx,
9758 1.1 christos sizeof(*glue));
9759 1.1 christos if (glue == NULL) {
9760 1.1 christos result = ISC_R_NOMEMORY;
9761 1.1 christos goto out;
9762 1.1 christos }
9763 1.1 christos
9764 1.1 christos gluename = dns_fixedname_initname(&glue->fixedname);
9765 1.1 christos dns_name_copy(name_aaaa, gluename, NULL);
9766 1.1 christos
9767 1.1 christos dns_rdataset_init(&glue->rdataset_a);
9768 1.1 christos dns_rdataset_init(&glue->sigrdataset_a);
9769 1.1 christos dns_rdataset_init(&glue->rdataset_aaaa);
9770 1.1 christos dns_rdataset_init(&glue->sigrdataset_aaaa);
9771 1.1 christos } else {
9772 1.1 christos INSIST(node_a == node_aaaa);
9773 1.1 christos INSIST(dns_name_equal(name_a, name_aaaa));
9774 1.1 christos }
9775 1.1 christos
9776 1.1 christos dns_rdataset_clone(&rdataset_aaaa, &glue->rdataset_aaaa);
9777 1.1 christos if (dns_rdataset_isassociated(&sigrdataset_aaaa)) {
9778 1.1 christos dns_rdataset_clone(&sigrdataset_aaaa,
9779 1.1 christos &glue->sigrdataset_aaaa);
9780 1.1 christos }
9781 1.1 christos }
9782 1.1 christos
9783 1.1 christos if (glue != NULL) {
9784 1.1 christos glue->next = ctx->glue_list;
9785 1.1 christos ctx->glue_list = glue;
9786 1.1 christos }
9787 1.1 christos
9788 1.1 christos result = ISC_R_SUCCESS;
9789 1.1 christos
9790 1.1 christos out:
9791 1.1 christos if (dns_rdataset_isassociated(&rdataset_a))
9792 1.1 christos rdataset_disassociate(&rdataset_a);
9793 1.1 christos if (dns_rdataset_isassociated(&sigrdataset_a))
9794 1.1 christos rdataset_disassociate(&sigrdataset_a);
9795 1.1 christos
9796 1.1 christos if (dns_rdataset_isassociated(&rdataset_aaaa))
9797 1.1 christos rdataset_disassociate(&rdataset_aaaa);
9798 1.1 christos if (dns_rdataset_isassociated(&sigrdataset_aaaa))
9799 1.1 christos rdataset_disassociate(&sigrdataset_aaaa);
9800 1.1 christos
9801 1.1 christos if (node_a != NULL)
9802 1.1 christos detachnode((dns_db_t *) ctx->rbtdb, (dns_dbnode_t *) &node_a);
9803 1.1 christos if (node_aaaa != NULL)
9804 1.1 christos detachnode((dns_db_t *) ctx->rbtdb,
9805 1.1 christos (dns_dbnode_t *) &node_aaaa);
9806 1.1 christos
9807 1.1 christos return (result);
9808 1.1 christos }
9809 1.1 christos
9810 1.1 christos static isc_result_t
9811 1.3 christos rdataset_addglue(dns_rdataset_t *rdataset, dns_dbversion_t *version,
9812 1.1 christos dns_message_t *msg)
9813 1.1 christos {
9814 1.1 christos dns_rbtdb_t *rbtdb = rdataset->private1;
9815 1.1 christos dns_rbtnode_t *node = rdataset->private2;
9816 1.1 christos rbtdb_version_t *rbtversion = version;
9817 1.3 christos uint32_t idx;
9818 1.1 christos rbtdb_glue_table_node_t *cur;
9819 1.3 christos bool found = false;
9820 1.3 christos bool restarted = false;
9821 1.1 christos rbtdb_glue_t *ge;
9822 1.1 christos rbtdb_glue_additionaldata_ctx_t ctx;
9823 1.1 christos isc_result_t result;
9824 1.1 christos
9825 1.1 christos REQUIRE(rdataset->type == dns_rdatatype_ns);
9826 1.1 christos REQUIRE(rbtdb == rbtversion->rbtdb);
9827 1.1 christos REQUIRE(!IS_CACHE(rbtdb) && !IS_STUB(rbtdb));
9828 1.1 christos
9829 1.1 christos /*
9830 1.1 christos * The glue table cache that forms a part of the DB version
9831 1.1 christos * structure is not explicitly bounded and there's no cache
9832 1.1 christos * cleaning. The zone data size itself is an implicit bound.
9833 1.1 christos *
9834 1.1 christos * The key into the glue hashtable is the node pointer. This is
9835 1.1 christos * because the glue hashtable is a property of the DB version,
9836 1.1 christos * and the glue is keyed for the ownername/NS tuple. We don't
9837 1.1 christos * bother with using an expensive dns_name_t comparison here as
9838 1.1 christos * the node pointer is a fixed value that won't change for a DB
9839 1.1 christos * version and can be compared directly.
9840 1.1 christos */
9841 1.3 christos idx = isc_hash_function(&node, sizeof(node), true, NULL) %
9842 1.1 christos rbtversion->glue_table_size;
9843 1.1 christos
9844 1.1 christos restart:
9845 1.1 christos /*
9846 1.1 christos * First, check if we have the additional entries already cached
9847 1.1 christos * in the glue table.
9848 1.1 christos */
9849 1.1 christos RWLOCK(&rbtversion->glue_rwlock, isc_rwlocktype_read);
9850 1.1 christos
9851 1.1 christos for (cur = rbtversion->glue_table[idx]; cur != NULL; cur = cur->next)
9852 1.1 christos if (cur->node == node)
9853 1.1 christos break;
9854 1.1 christos
9855 1.1 christos if (cur == NULL) {
9856 1.1 christos goto no_glue;
9857 1.1 christos }
9858 1.1 christos /*
9859 1.1 christos * We found a cached result. Add it to the message and
9860 1.1 christos * return.
9861 1.1 christos */
9862 1.3 christos found = true;
9863 1.1 christos ge = cur->glue_list;
9864 1.1 christos
9865 1.1 christos /*
9866 1.1 christos * (void *) -1 is a special value that means no glue is
9867 1.1 christos * present in the zone.
9868 1.1 christos */
9869 1.1 christos if (ge == (void *) -1) {
9870 1.1 christos if (!restarted && (rbtdb->gluecachestats != NULL)) {
9871 1.1 christos isc_stats_increment
9872 1.1 christos (rbtdb->gluecachestats,
9873 1.1 christos dns_gluecachestatscounter_hits_absent);
9874 1.1 christos }
9875 1.1 christos goto no_glue;
9876 1.1 christos } else {
9877 1.1 christos if (!restarted && (rbtdb->gluecachestats != NULL)) {
9878 1.1 christos isc_stats_increment
9879 1.1 christos (rbtdb->gluecachestats,
9880 1.1 christos dns_gluecachestatscounter_hits_present);
9881 1.1 christos }
9882 1.1 christos }
9883 1.1 christos
9884 1.1 christos for (; ge != NULL; ge = ge->next) {
9885 1.1 christos isc_buffer_t *buffer = NULL;
9886 1.1 christos dns_name_t *name = NULL;
9887 1.1 christos dns_rdataset_t *rdataset_a = NULL;
9888 1.1 christos dns_rdataset_t *sigrdataset_a = NULL;
9889 1.1 christos dns_rdataset_t *rdataset_aaaa = NULL;
9890 1.1 christos dns_rdataset_t *sigrdataset_aaaa = NULL;
9891 1.1 christos dns_name_t *gluename = dns_fixedname_name(&ge->fixedname);
9892 1.1 christos
9893 1.1 christos result = isc_buffer_allocate(msg->mctx, &buffer, 512);
9894 1.1 christos if (ISC_UNLIKELY(result != ISC_R_SUCCESS)) {
9895 1.1 christos goto no_glue;
9896 1.1 christos }
9897 1.1 christos
9898 1.1 christos result = dns_message_gettempname(msg, &name);
9899 1.1 christos if (ISC_UNLIKELY(result != ISC_R_SUCCESS)) {
9900 1.1 christos isc_buffer_free(&buffer);
9901 1.1 christos goto no_glue;
9902 1.1 christos }
9903 1.1 christos
9904 1.1 christos dns_name_copy(gluename, name, buffer);
9905 1.1 christos dns_message_takebuffer(msg, &buffer);
9906 1.1 christos
9907 1.1 christos if (dns_rdataset_isassociated(&ge->rdataset_a)) {
9908 1.1 christos result = dns_message_gettemprdataset(msg, &rdataset_a);
9909 1.1 christos if (ISC_UNLIKELY(result != ISC_R_SUCCESS)) {
9910 1.1 christos dns_message_puttempname(msg, &name);
9911 1.1 christos goto no_glue;
9912 1.1 christos }
9913 1.1 christos }
9914 1.1 christos
9915 1.1 christos if (dns_rdataset_isassociated(&ge->sigrdataset_a)) {
9916 1.1 christos result = dns_message_gettemprdataset(msg,
9917 1.1 christos &sigrdataset_a);
9918 1.1 christos if (ISC_UNLIKELY(result != ISC_R_SUCCESS)) {
9919 1.1 christos if (rdataset_a != NULL) {
9920 1.1 christos dns_message_puttemprdataset(msg,
9921 1.1 christos &rdataset_a);
9922 1.1 christos }
9923 1.1 christos dns_message_puttempname(msg, &name);
9924 1.1 christos goto no_glue;
9925 1.1 christos }
9926 1.1 christos }
9927 1.1 christos
9928 1.3 christos if (dns_rdataset_isassociated(&ge->rdataset_aaaa)) {
9929 1.3 christos result = dns_message_gettemprdataset(msg,
9930 1.3 christos &rdataset_aaaa);
9931 1.3 christos if (ISC_UNLIKELY(result != ISC_R_SUCCESS)) {
9932 1.3 christos dns_message_puttempname(msg, &name);
9933 1.3 christos if (rdataset_a != NULL) {
9934 1.3 christos dns_message_puttemprdataset(msg,
9935 1.3 christos &rdataset_a);
9936 1.3 christos }
9937 1.3 christos if (sigrdataset_a != NULL) {
9938 1.3 christos dns_message_puttemprdataset(msg,
9939 1.3 christos &sigrdataset_a);
9940 1.1 christos }
9941 1.3 christos goto no_glue;
9942 1.1 christos }
9943 1.3 christos }
9944 1.1 christos
9945 1.3 christos if (dns_rdataset_isassociated(&ge->sigrdataset_aaaa)) {
9946 1.3 christos result = dns_message_gettemprdataset(msg,
9947 1.3 christos &sigrdataset_aaaa);
9948 1.3 christos if (ISC_UNLIKELY(result != ISC_R_SUCCESS)) {
9949 1.3 christos dns_message_puttempname(msg, &name);
9950 1.3 christos if (rdataset_a != NULL) {
9951 1.3 christos dns_message_puttemprdataset(msg,
9952 1.3 christos &rdataset_a);
9953 1.1 christos }
9954 1.3 christos if (sigrdataset_a != NULL)
9955 1.3 christos dns_message_puttemprdataset(msg,
9956 1.3 christos &sigrdataset_a);
9957 1.3 christos if (rdataset_aaaa != NULL)
9958 1.3 christos dns_message_puttemprdataset(msg,
9959 1.3 christos &rdataset_aaaa);
9960 1.3 christos goto no_glue;
9961 1.1 christos }
9962 1.1 christos }
9963 1.1 christos
9964 1.1 christos if (ISC_LIKELY(rdataset_a != NULL)) {
9965 1.1 christos dns_rdataset_clone(&ge->rdataset_a, rdataset_a);
9966 1.1 christos ISC_LIST_APPEND(name->list, rdataset_a, link);
9967 1.1 christos }
9968 1.1 christos
9969 1.1 christos if (sigrdataset_a != NULL) {
9970 1.1 christos dns_rdataset_clone(&ge->sigrdataset_a, sigrdataset_a);
9971 1.1 christos ISC_LIST_APPEND(name->list, sigrdataset_a, link);
9972 1.1 christos }
9973 1.1 christos
9974 1.3 christos if (rdataset_aaaa != NULL) {
9975 1.3 christos dns_rdataset_clone(&ge->rdataset_aaaa,
9976 1.3 christos rdataset_aaaa);
9977 1.3 christos ISC_LIST_APPEND(name->list, rdataset_aaaa,
9978 1.3 christos link);
9979 1.3 christos }
9980 1.3 christos if (sigrdataset_aaaa != NULL) {
9981 1.3 christos dns_rdataset_clone(&ge->sigrdataset_aaaa,
9982 1.3 christos sigrdataset_aaaa);
9983 1.3 christos ISC_LIST_APPEND(name->list, sigrdataset_aaaa,
9984 1.3 christos link);
9985 1.1 christos }
9986 1.1 christos
9987 1.1 christos dns_message_addname(msg, name, DNS_SECTION_ADDITIONAL);
9988 1.1 christos }
9989 1.1 christos
9990 1.1 christos no_glue:
9991 1.1 christos RWUNLOCK(&rbtversion->glue_rwlock, isc_rwlocktype_read);
9992 1.1 christos
9993 1.1 christos if (found) {
9994 1.1 christos return (ISC_R_SUCCESS);
9995 1.1 christos }
9996 1.1 christos
9997 1.1 christos if (restarted) {
9998 1.1 christos return (ISC_R_FAILURE);
9999 1.1 christos }
10000 1.1 christos
10001 1.1 christos /*
10002 1.1 christos * No cached glue was found in the table. Cache it and restart
10003 1.1 christos * this function.
10004 1.1 christos *
10005 1.1 christos * Due to the gap between the read lock and the write lock, it's
10006 1.1 christos * possible that we may cache a duplicate glue table entry, but
10007 1.1 christos * we don't care.
10008 1.1 christos */
10009 1.1 christos
10010 1.1 christos ctx.glue_list = NULL;
10011 1.1 christos ctx.rbtdb = rbtdb;
10012 1.1 christos ctx.rbtversion = rbtversion;
10013 1.1 christos
10014 1.1 christos RWLOCK(&rbtversion->glue_rwlock, isc_rwlocktype_write);
10015 1.1 christos
10016 1.1 christos if (ISC_UNLIKELY(rehash_gluetable(rbtversion))) {
10017 1.1 christos idx = isc_hash_function(&node, sizeof(node),
10018 1.3 christos true, NULL) %
10019 1.1 christos rbtversion->glue_table_size;
10020 1.1 christos }
10021 1.1 christos
10022 1.1 christos (void)dns_rdataset_additionaldata(rdataset, glue_nsdname_cb, &ctx);
10023 1.1 christos
10024 1.1 christos cur = isc_mem_get(rbtdb->common.mctx, sizeof(*cur));
10025 1.1 christos if (cur == NULL) {
10026 1.1 christos result = ISC_R_NOMEMORY;
10027 1.1 christos goto out;
10028 1.1 christos }
10029 1.1 christos
10030 1.1 christos /*
10031 1.1 christos * XXXMUKS: it looks like the dns_dbversion is not destroyed
10032 1.1 christos * when named is terminated by a keyboard break. This doesn't
10033 1.1 christos * cleanup the node reference and keeps the process dangling.
10034 1.1 christos */
10035 1.3 christos /* isc_refcount_increment0(&node->references); */
10036 1.1 christos cur->node = node;
10037 1.1 christos
10038 1.1 christos if (ctx.glue_list == NULL) {
10039 1.1 christos /*
10040 1.1 christos * No glue was found. Cache it so.
10041 1.1 christos */
10042 1.1 christos cur->glue_list = (void *) -1;
10043 1.1 christos if (rbtdb->gluecachestats != NULL) {
10044 1.1 christos isc_stats_increment
10045 1.1 christos (rbtdb->gluecachestats,
10046 1.1 christos dns_gluecachestatscounter_inserts_absent);
10047 1.1 christos }
10048 1.1 christos } else {
10049 1.1 christos cur->glue_list = ctx.glue_list;
10050 1.1 christos if (rbtdb->gluecachestats != NULL) {
10051 1.1 christos isc_stats_increment
10052 1.1 christos (rbtdb->gluecachestats,
10053 1.1 christos dns_gluecachestatscounter_inserts_present);
10054 1.1 christos }
10055 1.1 christos }
10056 1.1 christos
10057 1.1 christos cur->next = rbtversion->glue_table[idx];
10058 1.1 christos rbtversion->glue_table[idx] = cur;
10059 1.1 christos rbtversion->glue_table_nodecount++;
10060 1.1 christos
10061 1.1 christos result = ISC_R_SUCCESS;
10062 1.1 christos
10063 1.1 christos out:
10064 1.1 christos RWUNLOCK(&rbtversion->glue_rwlock, isc_rwlocktype_write);
10065 1.1 christos
10066 1.1 christos if (result == ISC_R_SUCCESS) {
10067 1.3 christos restarted = true;
10068 1.1 christos goto restart;
10069 1.1 christos }
10070 1.1 christos
10071 1.1 christos return (result);
10072 1.1 christos }
10073 1.1 christos
10074 1.1 christos /*%
10075 1.1 christos * Routines for LRU-based cache management.
10076 1.1 christos */
10077 1.1 christos
10078 1.1 christos /*%
10079 1.1 christos * See if a given cache entry that is being reused needs to be updated
10080 1.1 christos * in the LRU-list. From the LRU management point of view, this function is
10081 1.1 christos * expected to return true for almost all cases. When used with threads,
10082 1.1 christos * however, this may cause a non-negligible performance penalty because a
10083 1.1 christos * writer lock will have to be acquired before updating the list.
10084 1.1 christos * If DNS_RBTDB_LIMITLRUUPDATE is defined to be non 0 at compilation time, this
10085 1.1 christos * function returns true if the entry has not been updated for some period of
10086 1.1 christos * time. We differentiate the NS or glue address case and the others since
10087 1.1 christos * experiments have shown that the former tends to be accessed relatively
10088 1.1 christos * infrequently and the cost of cache miss is higher (e.g., a missing NS records
10089 1.1 christos * may cause external queries at a higher level zone, involving more
10090 1.1 christos * transactions).
10091 1.1 christos *
10092 1.1 christos * Caller must hold the node (read or write) lock.
10093 1.1 christos */
10094 1.3 christos static inline bool
10095 1.1 christos need_headerupdate(rdatasetheader_t *header, isc_stdtime_t now) {
10096 1.1 christos if ((header->attributes &
10097 1.1 christos (RDATASET_ATTR_NONEXISTENT |
10098 1.1 christos RDATASET_ATTR_ANCIENT |
10099 1.1 christos RDATASET_ATTR_ZEROTTL)) != 0)
10100 1.3 christos return (false);
10101 1.1 christos
10102 1.1 christos #if DNS_RBTDB_LIMITLRUUPDATE
10103 1.1 christos if (header->type == dns_rdatatype_ns ||
10104 1.1 christos (header->trust == dns_trust_glue &&
10105 1.1 christos (header->type == dns_rdatatype_a ||
10106 1.1 christos header->type == dns_rdatatype_aaaa))) {
10107 1.1 christos /*
10108 1.1 christos * Glue records are updated if at least 60 seconds have passed
10109 1.1 christos * since the previous update time.
10110 1.1 christos */
10111 1.1 christos return (header->last_used + 60 <= now);
10112 1.1 christos }
10113 1.1 christos
10114 1.1 christos /* Other records are updated if 5 minutes have passed. */
10115 1.1 christos return (header->last_used + 300 <= now);
10116 1.1 christos #else
10117 1.1 christos UNUSED(now);
10118 1.1 christos
10119 1.3 christos return (true);
10120 1.1 christos #endif
10121 1.1 christos }
10122 1.1 christos
10123 1.1 christos /*%
10124 1.1 christos * Update the timestamp of a given cache entry and move it to the head
10125 1.1 christos * of the corresponding LRU list.
10126 1.1 christos *
10127 1.1 christos * Caller must hold the node (write) lock.
10128 1.1 christos *
10129 1.1 christos * Note that the we do NOT touch the heap here, as the TTL has not changed.
10130 1.1 christos */
10131 1.1 christos static void
10132 1.1 christos update_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
10133 1.1 christos isc_stdtime_t now)
10134 1.1 christos {
10135 1.1 christos INSIST(IS_CACHE(rbtdb));
10136 1.1 christos
10137 1.1 christos /* To be checked: can we really assume this? XXXMLG */
10138 1.1 christos INSIST(ISC_LINK_LINKED(header, link));
10139 1.1 christos
10140 1.1 christos ISC_LIST_UNLINK(rbtdb->rdatasets[header->node->locknum], header, link);
10141 1.1 christos header->last_used = now;
10142 1.1 christos ISC_LIST_PREPEND(rbtdb->rdatasets[header->node->locknum], header, link);
10143 1.1 christos }
10144 1.1 christos
10145 1.1 christos /*%
10146 1.1 christos * Purge some expired and/or stale (i.e. unused for some period) cache entries
10147 1.1 christos * under an overmem condition. To recover from this condition quickly, up to
10148 1.1 christos * 2 entries will be purged. This process is triggered while adding a new
10149 1.1 christos * entry, and we specifically avoid purging entries in the same LRU bucket as
10150 1.1 christos * the one to which the new entry will belong. Otherwise, we might purge
10151 1.1 christos * entries of the same name of different RR types while adding RRsets from a
10152 1.1 christos * single response (consider the case where we're adding A and AAAA glue records
10153 1.1 christos * of the same NS name).
10154 1.1 christos */
10155 1.1 christos static void
10156 1.1 christos overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start,
10157 1.3 christos isc_stdtime_t now, bool tree_locked)
10158 1.1 christos {
10159 1.1 christos rdatasetheader_t *header, *header_prev;
10160 1.1 christos unsigned int locknum;
10161 1.1 christos int purgecount = 2;
10162 1.1 christos
10163 1.1 christos for (locknum = (locknum_start + 1) % rbtdb->node_lock_count;
10164 1.1 christos locknum != locknum_start && purgecount > 0;
10165 1.1 christos locknum = (locknum + 1) % rbtdb->node_lock_count) {
10166 1.1 christos NODE_LOCK(&rbtdb->node_locks[locknum].lock,
10167 1.1 christos isc_rwlocktype_write);
10168 1.1 christos
10169 1.1 christos header = isc_heap_element(rbtdb->heaps[locknum], 1);
10170 1.1 christos if (header && header->rdh_ttl < now - RBTDB_VIRTUAL) {
10171 1.1 christos expire_header(rbtdb, header, tree_locked,
10172 1.1 christos expire_ttl);
10173 1.1 christos purgecount--;
10174 1.1 christos }
10175 1.1 christos
10176 1.1 christos for (header = ISC_LIST_TAIL(rbtdb->rdatasets[locknum]);
10177 1.1 christos header != NULL && purgecount > 0;
10178 1.1 christos header = header_prev) {
10179 1.1 christos header_prev = ISC_LIST_PREV(header, link);
10180 1.1 christos /*
10181 1.1 christos * Unlink the entry at this point to avoid checking it
10182 1.1 christos * again even if it's currently used someone else and
10183 1.1 christos * cannot be purged at this moment. This entry won't be
10184 1.1 christos * referenced any more (so unlinking is safe) since the
10185 1.1 christos * TTL was reset to 0.
10186 1.1 christos */
10187 1.1 christos ISC_LIST_UNLINK(rbtdb->rdatasets[locknum], header,
10188 1.1 christos link);
10189 1.1 christos expire_header(rbtdb, header, tree_locked,
10190 1.1 christos expire_lru);
10191 1.1 christos purgecount--;
10192 1.1 christos }
10193 1.1 christos
10194 1.1 christos NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
10195 1.1 christos isc_rwlocktype_write);
10196 1.1 christos }
10197 1.1 christos }
10198 1.1 christos
10199 1.1 christos static void
10200 1.1 christos expire_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
10201 1.3 christos bool tree_locked, expire_t reason)
10202 1.1 christos {
10203 1.1 christos set_ttl(rbtdb, header, 0);
10204 1.1 christos mark_header_ancient(rbtdb, header);
10205 1.1 christos
10206 1.1 christos /*
10207 1.1 christos * Caller must hold the node (write) lock.
10208 1.1 christos */
10209 1.1 christos
10210 1.3 christos if (isc_refcount_current(&header->node->references) == 0) {
10211 1.1 christos /*
10212 1.1 christos * If no one else is using the node, we can clean it up now.
10213 1.1 christos * We first need to gain a new reference to the node to meet a
10214 1.1 christos * requirement of decrement_reference().
10215 1.1 christos */
10216 1.1 christos new_reference(rbtdb, header->node);
10217 1.1 christos decrement_reference(rbtdb, header->node, 0,
10218 1.1 christos isc_rwlocktype_write,
10219 1.1 christos tree_locked ? isc_rwlocktype_write :
10220 1.3 christos isc_rwlocktype_none, false);
10221 1.1 christos
10222 1.1 christos if (rbtdb->cachestats == NULL)
10223 1.1 christos return;
10224 1.1 christos
10225 1.1 christos switch (reason) {
10226 1.1 christos case expire_ttl:
10227 1.1 christos isc_stats_increment(rbtdb->cachestats,
10228 1.1 christos dns_cachestatscounter_deletettl);
10229 1.1 christos break;
10230 1.1 christos case expire_lru:
10231 1.1 christos isc_stats_increment(rbtdb->cachestats,
10232 1.1 christos dns_cachestatscounter_deletelru);
10233 1.1 christos break;
10234 1.1 christos default:
10235 1.1 christos break;
10236 1.1 christos }
10237 1.1 christos
10238 1.1 christos }
10239 1.1 christos }
10240