rbt.c revision 1.2.2.2 1 /* $NetBSD: rbt.c,v 1.2.2.2 2018/09/06 06:55:00 pgoyette Exp $ */
2
3 /*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public
7 * License, v. 2.0. If a copy of the MPL was not distributed with this
8 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 *
10 * See the COPYRIGHT file distributed with this work for additional
11 * information regarding copyright ownership.
12 */
13
14 /*! \file */
15
16 #include <config.h>
17
18 #include <sys/stat.h>
19 #ifdef HAVE_INTTYPES_H
20 #include <inttypes.h> /* uintptr_t */
21 #endif
22
23 #include <isc/crc64.h>
24 #include <isc/file.h>
25 #include <isc/hex.h>
26 #include <isc/mem.h>
27 #include <isc/once.h>
28 #include <isc/platform.h>
29 #include <isc/print.h>
30 #include <isc/refcount.h>
31 #include <isc/socket.h>
32 #include <isc/stdio.h>
33 #include <isc/string.h>
34 #include <isc/util.h>
35
36 /*%
37 * This define is so dns/name.h (included by dns/fixedname.h) uses more
38 * efficient macro calls instead of functions for a few operations.
39 */
40 #define DNS_NAME_USEINLINE 1
41
42 #include <dns/fixedname.h>
43 #include <dns/log.h>
44 #include <dns/rbt.h>
45 #include <dns/result.h>
46 #include <dns/version.h>
47
48 #include <unistd.h>
49
50 #define CHECK(x) \
51 do { \
52 result = (x); \
53 if (result != ISC_R_SUCCESS) \
54 goto cleanup; \
55 } while (/*CONSTCOND*/0)
56
57 #define RBT_MAGIC ISC_MAGIC('R', 'B', 'T', '+')
58 #define VALID_RBT(rbt) ISC_MAGIC_VALID(rbt, RBT_MAGIC)
59
60 /*
61 * XXXDCL Since parent pointers were added in again, I could remove all of the
62 * chain junk, and replace with dns_rbt_firstnode, _previousnode, _nextnode,
63 * _lastnode. This would involve pretty major change to the API.
64 */
65 #define CHAIN_MAGIC ISC_MAGIC('0', '-', '0', '-')
66 #define VALID_CHAIN(chain) ISC_MAGIC_VALID(chain, CHAIN_MAGIC)
67
68 #define RBT_HASH_SIZE 64
69
70 #ifdef RBT_MEM_TEST
71 #undef RBT_HASH_SIZE
72 #define RBT_HASH_SIZE 2 /*%< To give the reallocation code a workout. */
73 #endif
74
75 struct dns_rbt {
76 unsigned int magic;
77 isc_mem_t * mctx;
78 dns_rbtnode_t * root;
79 void (*data_deleter)(void *, void *);
80 void * deleter_arg;
81 unsigned int nodecount;
82 size_t hashsize;
83 dns_rbtnode_t ** hashtable;
84 void * mmap_location;
85 };
86
87 #define RED 0
88 #define BLACK 1
89
90 /*
91 * This is the header for map-format RBT images. It is populated,
92 * and then written, as the LAST thing done to the file before returning.
93 * Writing this last (with zeros in the header area initially) will ensure
94 * that the header is only valid when the RBT image is also valid.
95 */
96 typedef struct file_header file_header_t;
97
98 /* Pad to 32 bytes */
99 static char FILE_VERSION[32] = "\0";
100
101 /* Header length, always the same size regardless of structure size */
102 #define HEADER_LENGTH 1024
103
104 struct file_header {
105 char version1[32];
106 isc_uint64_t first_node_offset; /* usually 1024 */
107 /*
108 * information about the system on which the map file was generated
109 * will be used to tell if we can load the map file or not
110 */
111 isc_uint32_t ptrsize;
112 unsigned int bigendian:1; /* big or little endian system */
113 unsigned int rdataset_fixed:1; /* compiled with --enable-rrset-fixed */
114 unsigned int nodecount; /* shadow from rbt structure */
115 isc_uint64_t crc;
116 char version2[32]; /* repeated; must match version1 */
117 };
118
119 /*
120 * The following declarations are for the serialization of an RBT:
121 *
122 * step one: write out a zeroed header of 1024 bytes
123 * step two: walk the tree in a depth-first, left-right-down order, writing
124 * out the nodes, reserving space as we go, correcting addresses to point
125 * at the proper offset in the file, and setting a flag for each pointer to
126 * indicate that it is a reference to a location in the file, rather than in
127 * memory.
128 * step three: write out the header, adding the information that will be
129 * needed to re-create the tree object itself.
130 *
131 * The RBTDB object will do this three times, once for each of the three
132 * RBT objects it contains.
133 *
134 * Note: 'file' must point an actual open file that can be mmapped
135 * and fseeked, not to a pipe or stream
136 */
137
138 static isc_result_t
139 dns_rbt_zero_header(FILE *file);
140
141 static isc_result_t
142 write_header(FILE *file, dns_rbt_t *rbt, isc_uint64_t first_node_offset,
143 isc_uint64_t crc);
144
145 static isc_boolean_t
146 match_header_version(file_header_t *header);
147
148 static isc_result_t
149 serialize_node(FILE *file, dns_rbtnode_t *node, uintptr_t left,
150 uintptr_t right, uintptr_t down, uintptr_t parent,
151 uintptr_t data, isc_uint64_t *crc);
152
153 static isc_result_t
154 serialize_nodes(FILE *file, dns_rbtnode_t *node, uintptr_t parent,
155 dns_rbtdatawriter_t datawriter, void *writer_arg,
156 uintptr_t *where, isc_uint64_t *crc);
157 /*
158 * The following functions allow you to get the actual address of a pointer
159 * without having to use an if statement to check to see if that address is
160 * relative or not
161 */
162 static inline dns_rbtnode_t *
163 getparent(dns_rbtnode_t *node, file_header_t *header) {
164 char *adjusted_address = (char *)(node->parent);
165 adjusted_address += node->parent_is_relative * (uintptr_t)header;
166
167 return ((dns_rbtnode_t *)adjusted_address);
168 }
169
170 static inline dns_rbtnode_t *
171 getleft(dns_rbtnode_t *node, file_header_t *header) {
172 char *adjusted_address = (char *)(node->left);
173 adjusted_address += node->left_is_relative * (uintptr_t)header;
174
175 return ((dns_rbtnode_t *)adjusted_address);
176 }
177
178 static inline dns_rbtnode_t *
179 getright(dns_rbtnode_t *node, file_header_t *header) {
180 char *adjusted_address = (char *)(node->right);
181 adjusted_address += node->right_is_relative * (uintptr_t)header;
182
183 return ((dns_rbtnode_t *)adjusted_address);
184 }
185
186 static inline dns_rbtnode_t *
187 getdown(dns_rbtnode_t *node, file_header_t *header) {
188 char *adjusted_address = (char *)(node->down);
189 adjusted_address += node->down_is_relative * (uintptr_t)header;
190
191 return ((dns_rbtnode_t *)adjusted_address);
192 }
193
194 static inline dns_rbtnode_t *
195 getdata(dns_rbtnode_t *node, file_header_t *header) {
196 char *adjusted_address = (char *)(node->data);
197 adjusted_address += node->data_is_relative * (uintptr_t)header;
198
199 return ((dns_rbtnode_t *)adjusted_address);
200 }
201
202 /*%
203 * Elements of the rbtnode structure.
204 */
205 #define PARENT(node) ((node)->parent)
206 #define LEFT(node) ((node)->left)
207 #define RIGHT(node) ((node)->right)
208 #define DOWN(node) ((node)->down)
209 #ifdef DNS_RBT_USEHASH
210 #define UPPERNODE(node) ((node)->uppernode)
211 #endif /* DNS_RBT_USEHASH */
212 #define DATA(node) ((node)->data)
213 #define IS_EMPTY(node) ((node)->data == NULL)
214 #define HASHNEXT(node) ((node)->hashnext)
215 #define HASHVAL(node) ((node)->hashval)
216 #define COLOR(node) ((node)->color)
217 #define NAMELEN(node) ((node)->namelen)
218 #define OLDNAMELEN(node) ((node)->oldnamelen)
219 #define OFFSETLEN(node) ((node)->offsetlen)
220 #define ATTRS(node) ((node)->attributes)
221 #define IS_ROOT(node) ISC_TF((node)->is_root == 1)
222 #define FINDCALLBACK(node) ISC_TF((node)->find_callback == 1)
223
224 /*%
225 * Structure elements from the rbtdb.c, not
226 * used as part of the rbt.c algorithms.
227 */
228 #define DIRTY(node) ((node)->dirty)
229 #define WILD(node) ((node)->wild)
230 #define LOCKNUM(node) ((node)->locknum)
231
232 /*%
233 * The variable length stuff stored after the node has the following
234 * structure.
235 *
236 * <name_data>{1..255}<oldoffsetlen>{1}<offsets>{1..128}
237 *
238 * <name_data> contains the name of the node when it was created.
239 * <oldoffsetlen> contains the length of <offsets> when the node was created.
240 * <offsets> contains the offets into name for each label when the node was
241 * created.
242 */
243
244 #define NAME(node) ((unsigned char *)((node) + 1))
245 #define OFFSETS(node) (NAME(node) + OLDNAMELEN(node) + 1)
246 #define OLDOFFSETLEN(node) (OFFSETS(node)[-1])
247
248 #define NODE_SIZE(node) (sizeof(*node) + \
249 OLDNAMELEN(node) + OLDOFFSETLEN(node) + 1)
250
251 /*%
252 * Color management.
253 */
254 #define IS_RED(node) ((node) != NULL && (node)->color == RED)
255 #define IS_BLACK(node) ((node) == NULL || (node)->color == BLACK)
256 #define MAKE_RED(node) ((node)->color = RED)
257 #define MAKE_BLACK(node) ((node)->color = BLACK)
258
259 /*%
260 * Chain management.
261 *
262 * The "ancestors" member of chains were removed, with their job now
263 * being wholly handled by parent pointers (which didn't exist, because
264 * of memory concerns, when chains were first implemented).
265 */
266 #define ADD_LEVEL(chain, node) \
267 do { \
268 INSIST((chain)->level_count < DNS_RBT_LEVELBLOCK); \
269 (chain)->levels[(chain)->level_count++] = (node); \
270 } while (0)
271
272 /*%
273 * The following macros directly access normally private name variables.
274 * These macros are used to avoid a lot of function calls in the critical
275 * path of the tree traversal code.
276 */
277
278 static inline void
279 NODENAME(dns_rbtnode_t *node, dns_name_t *name) {
280 name->length = NAMELEN(node);
281 name->labels = OFFSETLEN(node);
282 name->ndata = NAME(node);
283 name->offsets = OFFSETS(node);
284 name->attributes = ATTRS(node);
285 name->attributes |= DNS_NAMEATTR_READONLY;
286 }
287
288 void
289 dns_rbtnode_nodename(dns_rbtnode_t *node, dns_name_t *name) {
290 name->length = NAMELEN(node);
291 name->labels = OFFSETLEN(node);
292 name->ndata = NAME(node);
293 name->offsets = OFFSETS(node);
294 name->attributes = ATTRS(node);
295 name->attributes |= DNS_NAMEATTR_READONLY;
296 }
297
298 dns_rbtnode_t *
299 dns_rbt_root(dns_rbt_t *rbt) {
300 return rbt->root;
301 }
302
303 #ifdef DNS_RBT_USEHASH
304 static isc_result_t
305 inithash(dns_rbt_t *rbt);
306 #endif
307
308 #ifdef DEBUG
309 #define inline
310 /*
311 * A little something to help out in GDB.
312 */
313 dns_name_t Name(dns_rbtnode_t *node);
314 dns_name_t
315 Name(dns_rbtnode_t *node) {
316 dns_name_t name;
317
318 dns_name_init(&name, NULL);
319 if (node != NULL)
320 NODENAME(node, &name);
321
322 return (name);
323 }
324
325 static void
326 hexdump(const char *desc, void *blob, size_t size) {
327 char hexdump[BUFSIZ * 2 + 1];
328 isc_buffer_t b;
329 isc_region_t r;
330 isc_result_t result;
331 size_t bytes;
332 isc_uint8_t *data = blob;
333
334 fprintf(stderr, "%s: ", desc);
335 do {
336 isc_buffer_init(&b, hexdump, sizeof(hexdump));
337 r.base = data;
338 r.length = bytes = (size > BUFSIZ) ? BUFSIZ : size;
339 result = isc_hex_totext(&r, 0, "", &b);
340 RUNTIME_CHECK(result == ISC_R_SUCCESS);
341 isc_buffer_putuint8(&b, 0);
342 fprintf(stderr, "%s", hexdump);
343 data += bytes;
344 size -= bytes;
345 } while (size > 0);
346 fprintf(stderr, "\n");
347 }
348 #endif /* DEBUG */
349
350 #ifdef DNS_RBT_USEHASH
351
352 /*
353 * Upper node is the parent of the root of the passed node's
354 * subtree. The passed node must not be NULL.
355 */
356 static inline dns_rbtnode_t *
357 get_upper_node(dns_rbtnode_t *node) {
358 return (UPPERNODE(node));
359 }
360
361 static void
362 fixup_uppernodes_helper(dns_rbtnode_t *node, dns_rbtnode_t *uppernode) {
363 if (node == NULL)
364 return;
365
366 UPPERNODE(node) = uppernode;
367
368 fixup_uppernodes_helper(LEFT(node), uppernode);
369 fixup_uppernodes_helper(RIGHT(node), uppernode);
370 fixup_uppernodes_helper(DOWN(node), node);
371 }
372
373 /*
374 * This function is used to fixup uppernode members of all dns_rbtnodes
375 * after deserialization.
376 */
377 static void
378 fixup_uppernodes(dns_rbt_t *rbt) {
379 fixup_uppernodes_helper(rbt->root, NULL);
380 }
381
382 #else
383
384 /* The passed node must not be NULL. */
385 static inline dns_rbtnode_t *
386 get_subtree_root(dns_rbtnode_t *node) {
387 while (!IS_ROOT(node)) {
388 node = PARENT(node);
389 }
390
391 return (node);
392 }
393
394 /* Upper node is the parent of the root of the passed node's
395 * subtree. The passed node must not be NULL.
396 */
397 static inline dns_rbtnode_t *
398 get_upper_node(dns_rbtnode_t *node) {
399 dns_rbtnode_t *root = get_subtree_root(node);
400
401 /*
402 * Return the node in the level above the argument node that points
403 * to the level the argument node is in. If the argument node is in
404 * the top level, the return value is NULL.
405 */
406 return (PARENT(root));
407 }
408
409 #endif /* DNS_RBT_USEHASH */
410
411 size_t
412 dns__rbtnode_getdistance(dns_rbtnode_t *node) {
413 size_t nodes = 1;
414
415 while (node != NULL) {
416 if (IS_ROOT(node))
417 break;
418 nodes++;
419 node = PARENT(node);
420 }
421
422 return (nodes);
423 }
424
425 /*
426 * Forward declarations.
427 */
428 static isc_result_t
429 create_node(isc_mem_t *mctx, const dns_name_t *name, dns_rbtnode_t **nodep);
430
431 #ifdef DNS_RBT_USEHASH
432 static inline void
433 hash_node(dns_rbt_t *rbt, dns_rbtnode_t *node, const dns_name_t *name);
434 static inline void
435 unhash_node(dns_rbt_t *rbt, dns_rbtnode_t *node);
436 static void
437 rehash(dns_rbt_t *rbt, unsigned int newcount);
438 #else
439 #define hash_node(rbt, node, name)
440 #define unhash_node(rbt, node)
441 #define rehash(rbt, newcount)
442 #endif
443
444 static inline void
445 rotate_left(dns_rbtnode_t *node, dns_rbtnode_t **rootp);
446 static inline void
447 rotate_right(dns_rbtnode_t *node, dns_rbtnode_t **rootp);
448
449 static void
450 addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order,
451 dns_rbtnode_t **rootp);
452
453 static void
454 deletefromlevel(dns_rbtnode_t *item, dns_rbtnode_t **rootp);
455
456 static isc_result_t
457 treefix(dns_rbt_t *rbt, void *base, size_t size,
458 dns_rbtnode_t *n, const dns_name_t *name,
459 dns_rbtdatafixer_t datafixer, void *fixer_arg,
460 isc_uint64_t *crc);
461
462 static void
463 deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, isc_boolean_t unhash,
464 dns_rbtnode_t **nodep);
465
466 static void
467 printnodename(dns_rbtnode_t *node, isc_boolean_t quoted, FILE *f);
468
469 static void
470 freenode(dns_rbt_t *rbt, dns_rbtnode_t **nodep);
471
472 static isc_result_t
473 dns_rbt_zero_header(FILE *file) {
474 /*
475 * Write out a zeroed header as a placeholder. Doing this ensures
476 * that the file will not read while it is partially written, should
477 * writing fail or be interrupted.
478 */
479 char buffer[HEADER_LENGTH];
480 isc_result_t result;
481
482 memset(buffer, 0, HEADER_LENGTH);
483 result = isc_stdio_write(buffer, 1, HEADER_LENGTH, file, NULL);
484 if (result != ISC_R_SUCCESS)
485 return (result);
486
487 result = fflush(file);
488 if (result != ISC_R_SUCCESS)
489 return (result);
490
491 return (ISC_R_SUCCESS);
492 }
493
494 static isc_once_t once = ISC_ONCE_INIT;
495
496 static void
497 init_file_version(void) {
498 int n;
499
500 memset(FILE_VERSION, 0, sizeof(FILE_VERSION));
501 n = snprintf(FILE_VERSION, sizeof(FILE_VERSION),
502 "RBT Image %s %s", dns_major, dns_mapapi);
503 INSIST(n > 0 && (unsigned int)n < sizeof(FILE_VERSION));
504 }
505
506 /*
507 * Write out the real header, including NodeDump version information
508 * and the offset of the first node.
509 *
510 * Any information stored in the rbt object itself should be stored
511 * here.
512 */
513 static isc_result_t
514 write_header(FILE *file, dns_rbt_t *rbt, isc_uint64_t first_node_offset,
515 isc_uint64_t crc)
516 {
517 file_header_t header;
518 isc_result_t result;
519 off_t location;
520
521 RUNTIME_CHECK(isc_once_do(&once, init_file_version) == ISC_R_SUCCESS);
522
523 memset(&header, 0, sizeof(file_header_t));
524 memmove(header.version1, FILE_VERSION, sizeof(header.version1));
525 memmove(header.version2, FILE_VERSION, sizeof(header.version2));
526 header.first_node_offset = first_node_offset;
527 header.ptrsize = (isc_uint32_t) sizeof(void *);
528 header.bigendian = (1 == htonl(1)) ? 1 : 0;
529
530 #ifdef DNS_RDATASET_FIXED
531 header.rdataset_fixed = 1;
532 #else
533 header.rdataset_fixed = 0;
534 #endif
535
536 header.nodecount = rbt->nodecount;
537
538 header.crc = crc;
539
540 CHECK(isc_stdio_tell(file, &location));
541 location = dns_rbt_serialize_align(location);
542 CHECK(isc_stdio_seek(file, location, SEEK_SET));
543 CHECK(isc_stdio_write(&header, 1, sizeof(file_header_t), file, NULL));
544 CHECK(fflush(file));
545
546 /* Ensure we are always at the end of the file. */
547 CHECK(isc_stdio_seek(file, 0, SEEK_END));
548
549 cleanup:
550 return (result);
551 }
552
553 static isc_boolean_t
554 match_header_version(file_header_t *header) {
555 RUNTIME_CHECK(isc_once_do(&once, init_file_version) == ISC_R_SUCCESS);
556
557 if (memcmp(header->version1, FILE_VERSION,
558 sizeof(header->version1)) != 0 ||
559 memcmp(header->version2, FILE_VERSION,
560 sizeof(header->version1)) != 0)
561 {
562 return (ISC_FALSE);
563 }
564
565 return (ISC_TRUE);
566 }
567
568 static isc_result_t
569 serialize_node(FILE *file, dns_rbtnode_t *node, uintptr_t left,
570 uintptr_t right, uintptr_t down, uintptr_t parent,
571 uintptr_t data, isc_uint64_t *crc)
572 {
573 dns_rbtnode_t temp_node;
574 off_t file_position;
575 unsigned char *node_data;
576 size_t datasize;
577 isc_result_t result;
578 #ifdef DEBUG
579 dns_name_t nodename;
580 #endif
581
582 INSIST(node != NULL);
583
584 CHECK(isc_stdio_tell(file, &file_position));
585 file_position = dns_rbt_serialize_align(file_position);
586 CHECK(isc_stdio_seek(file, file_position, SEEK_SET));
587
588 temp_node = *node;
589 temp_node.down_is_relative = 0;
590 temp_node.left_is_relative = 0;
591 temp_node.right_is_relative = 0;
592 temp_node.parent_is_relative = 0;
593 temp_node.data_is_relative = 0;
594 temp_node.is_mmapped = 1;
595
596 /*
597 * If the next node is not NULL, calculate the next node's location
598 * in the file. Note that this will have to change when the data
599 * structure changes, and it also assumes that we always write the
600 * nodes out in list order (which we currently do.)
601 */
602 if (temp_node.parent != NULL) {
603 temp_node.parent = (dns_rbtnode_t *)(parent);
604 temp_node.parent_is_relative = 1;
605 }
606 if (temp_node.left != NULL) {
607 temp_node.left = (dns_rbtnode_t *)(left);
608 temp_node.left_is_relative = 1;
609 }
610 if (temp_node.right != NULL) {
611 temp_node.right = (dns_rbtnode_t *)(right);
612 temp_node.right_is_relative = 1;
613 }
614 if (temp_node.down != NULL) {
615 temp_node.down = (dns_rbtnode_t *)(down);
616 temp_node.down_is_relative = 1;
617 }
618 if (temp_node.data != NULL) {
619 temp_node.data = (dns_rbtnode_t *)(data);
620 temp_node.data_is_relative = 1;
621 }
622
623 node_data = (unsigned char *) node + sizeof(dns_rbtnode_t);
624 datasize = NODE_SIZE(node) - sizeof(dns_rbtnode_t);
625
626 CHECK(isc_stdio_write(&temp_node, 1, sizeof(dns_rbtnode_t),
627 file, NULL));
628 CHECK(isc_stdio_write(node_data, 1, datasize, file, NULL));
629
630 #ifdef DEBUG
631 dns_name_init(&nodename, NULL);
632 NODENAME(node, &nodename);
633 fprintf(stderr, "serialize ");
634 dns_name_print(&nodename, stderr);
635 fprintf(stderr, "\n");
636 hexdump("node header", &temp_node,
637 sizeof(dns_rbtnode_t));
638 hexdump("node data", node_data, datasize);
639 #endif
640
641 isc_crc64_update(crc, (const isc_uint8_t *) &temp_node,
642 sizeof(dns_rbtnode_t));
643 isc_crc64_update(crc, (const isc_uint8_t *) node_data, datasize);
644
645 cleanup:
646 return (result);
647 }
648
649 static isc_result_t
650 serialize_nodes(FILE *file, dns_rbtnode_t *node, uintptr_t parent,
651 dns_rbtdatawriter_t datawriter, void *writer_arg,
652 uintptr_t *where, isc_uint64_t *crc)
653 {
654 uintptr_t left = 0, right = 0, down = 0, data = 0;
655 off_t location = 0, offset_adjust;
656 isc_result_t result;
657
658 if (node == NULL) {
659 if (where != NULL)
660 *where = 0;
661 return (ISC_R_SUCCESS);
662 }
663
664 /* Reserve space for current node. */
665 CHECK(isc_stdio_tell(file, &location));
666 location = dns_rbt_serialize_align(location);
667 CHECK(isc_stdio_seek(file, location, SEEK_SET));
668
669 offset_adjust = dns_rbt_serialize_align(location + NODE_SIZE(node));
670 CHECK(isc_stdio_seek(file, offset_adjust, SEEK_SET));
671
672 /*
673 * Serialize the rest of the tree.
674 *
675 * WARNING: A change in the order (from left, right, down)
676 * will break the way the crc hash is computed.
677 */
678 CHECK(serialize_nodes(file, getleft(node, NULL), location,
679 datawriter, writer_arg, &left, crc));
680 CHECK(serialize_nodes(file, getright(node, NULL), location,
681 datawriter, writer_arg, &right, crc));
682 CHECK(serialize_nodes(file, getdown(node, NULL), location,
683 datawriter, writer_arg, &down, crc));
684
685 if (node->data != NULL) {
686 off_t ret;
687
688 CHECK(isc_stdio_tell(file, &ret));
689 ret = dns_rbt_serialize_align(ret);
690 CHECK(isc_stdio_seek(file, ret, SEEK_SET));
691 data = ret;
692
693 datawriter(file, node->data, writer_arg, crc);
694 }
695
696 /* Seek back to reserved space. */
697 CHECK(isc_stdio_seek(file, location, SEEK_SET));
698
699 /* Serialize the current node. */
700 CHECK(serialize_node(file, node, left, right, down, parent, data, crc));
701
702 /* Ensure we are always at the end of the file. */
703 CHECK(isc_stdio_seek(file, 0, SEEK_END));
704
705 if (where != NULL)
706 *where = (uintptr_t) location;
707
708 cleanup:
709 return (result);
710 }
711
712 off_t
713 dns_rbt_serialize_align(off_t target) {
714 off_t offset = target % 8;
715
716 if (offset == 0)
717 return (target);
718 else
719 return (target + 8 - offset);
720 }
721
722 isc_result_t
723 dns_rbt_serialize_tree(FILE *file, dns_rbt_t *rbt,
724 dns_rbtdatawriter_t datawriter,
725 void *writer_arg, off_t *offset)
726 {
727 isc_result_t result;
728 off_t header_position, node_position, end_position;
729 isc_uint64_t crc;
730
731 REQUIRE(file != NULL);
732
733 CHECK(isc_file_isplainfilefd(fileno(file)));
734
735 isc_crc64_init(&crc);
736
737 CHECK(isc_stdio_tell(file, &header_position));
738
739 /* Write dummy header */
740 CHECK(dns_rbt_zero_header(file));
741
742 /* Serialize nodes */
743 CHECK(isc_stdio_tell(file, &node_position));
744 CHECK(serialize_nodes(file, rbt->root, 0, datawriter,
745 writer_arg, NULL, &crc));
746
747 CHECK(isc_stdio_tell(file, &end_position));
748 if (node_position == end_position) {
749 CHECK(isc_stdio_seek(file, header_position, SEEK_SET));
750 *offset = 0;
751 return (ISC_R_SUCCESS);
752 }
753
754 isc_crc64_final(&crc);
755 #ifdef DEBUG
756 hexdump("serializing CRC", &crc, sizeof(crc));
757 #endif
758
759 /* Serialize header */
760 CHECK(isc_stdio_seek(file, header_position, SEEK_SET));
761 CHECK(write_header(file, rbt, HEADER_LENGTH, crc));
762
763 /* Ensure we are always at the end of the file. */
764 CHECK(isc_stdio_seek(file, 0, SEEK_END));
765 *offset = dns_rbt_serialize_align(header_position);
766
767 cleanup:
768 return (result);
769 }
770
771 #define CONFIRM(a) do { \
772 if (! (a)) { \
773 result = ISC_R_INVALIDFILE; \
774 goto cleanup; \
775 } \
776 } while(/*CONSTCOND*/0);
777
778 static isc_result_t
779 treefix(dns_rbt_t *rbt, void *base, size_t filesize, dns_rbtnode_t *n,
780 const dns_name_t *name, dns_rbtdatafixer_t datafixer,
781 void *fixer_arg, isc_uint64_t *crc)
782 {
783 isc_result_t result = ISC_R_SUCCESS;
784 dns_fixedname_t fixed;
785 dns_name_t nodename, *fullname;
786 unsigned char *node_data;
787 dns_rbtnode_t header;
788 size_t datasize, nodemax = filesize - sizeof(dns_rbtnode_t);
789
790 if (n == NULL)
791 return (ISC_R_SUCCESS);
792
793 CONFIRM((void *) n >= base);
794 CONFIRM((char *) n - (char *) base <= (int) nodemax);
795 CONFIRM(DNS_RBTNODE_VALID(n));
796
797 dns_name_init(&nodename, NULL);
798 NODENAME(n, &nodename);
799
800 fullname = &nodename;
801 CONFIRM(dns_name_isvalid(fullname));
802
803 if (!dns_name_isabsolute(&nodename)) {
804 fullname = dns_fixedname_initname(&fixed);
805 CHECK(dns_name_concatenate(&nodename, name, fullname, NULL));
806 }
807
808 /* memorize header contents prior to fixup */
809 memmove(&header, n, sizeof(header));
810
811 if (n->left_is_relative) {
812 CONFIRM(n->left <= (dns_rbtnode_t *) nodemax);
813 n->left = getleft(n, rbt->mmap_location);
814 n->left_is_relative = 0;
815 CONFIRM(DNS_RBTNODE_VALID(n->left));
816 } else
817 CONFIRM(n->left == NULL);
818
819 if (n->right_is_relative) {
820 CONFIRM(n->right <= (dns_rbtnode_t *) nodemax);
821 n->right = getright(n, rbt->mmap_location);
822 n->right_is_relative = 0;
823 CONFIRM(DNS_RBTNODE_VALID(n->right));
824 } else
825 CONFIRM(n->right == NULL);
826
827 if (n->down_is_relative) {
828 CONFIRM(n->down <= (dns_rbtnode_t *) nodemax);
829 n->down = getdown(n, rbt->mmap_location);
830 n->down_is_relative = 0;
831 CONFIRM(n->down > (dns_rbtnode_t *) n);
832 CONFIRM(DNS_RBTNODE_VALID(n->down));
833 } else
834 CONFIRM(n->down == NULL);
835
836 if (n->parent_is_relative) {
837 CONFIRM(n->parent <= (dns_rbtnode_t *) nodemax);
838 n->parent = getparent(n, rbt->mmap_location);
839 n->parent_is_relative = 0;
840 CONFIRM(n->parent < (dns_rbtnode_t *) n);
841 CONFIRM(DNS_RBTNODE_VALID(n->parent));
842 } else
843 CONFIRM(n->parent == NULL);
844
845 if (n->data_is_relative) {
846 CONFIRM(n->data <= (void *) filesize);
847 n->data = getdata(n, rbt->mmap_location);
848 n->data_is_relative = 0;
849 CONFIRM(n->data > (void *) n);
850 } else
851 CONFIRM(n->data == NULL);
852
853 hash_node(rbt, n, fullname);
854
855 /* a change in the order (from left, right, down) will break hashing*/
856 if (n->left != NULL)
857 CHECK(treefix(rbt, base, filesize, n->left, name,
858 datafixer, fixer_arg, crc));
859 if (n->right != NULL)
860 CHECK(treefix(rbt, base, filesize, n->right, name,
861 datafixer, fixer_arg, crc));
862 if (n->down != NULL)
863 CHECK(treefix(rbt, base, filesize, n->down, fullname,
864 datafixer, fixer_arg, crc));
865
866 if (datafixer != NULL && n->data != NULL)
867 CHECK(datafixer(n, base, filesize, fixer_arg, crc));
868
869 rbt->nodecount++;
870 node_data = (unsigned char *) n + sizeof(dns_rbtnode_t);
871 datasize = NODE_SIZE(n) - sizeof(dns_rbtnode_t);
872
873 #ifdef DEBUG
874 fprintf(stderr, "deserialize ");
875 dns_name_print(&nodename, stderr);
876 fprintf(stderr, "\n");
877 hexdump("node header", &header,
878 sizeof(dns_rbtnode_t));
879 hexdump("node data", node_data, datasize);
880 #endif
881 isc_crc64_update(crc, (const isc_uint8_t *) &header,
882 sizeof(dns_rbtnode_t));
883 isc_crc64_update(crc, (const isc_uint8_t *) node_data,
884 datasize);
885
886 cleanup:
887 return (result);
888 }
889
890 isc_result_t
891 dns_rbt_deserialize_tree(void *base_address, size_t filesize,
892 off_t header_offset, isc_mem_t *mctx,
893 dns_rbtdeleter_t deleter, void *deleter_arg,
894 dns_rbtdatafixer_t datafixer, void *fixer_arg,
895 dns_rbtnode_t **originp, dns_rbt_t **rbtp)
896 {
897 isc_result_t result = ISC_R_SUCCESS;
898 file_header_t *header;
899 dns_rbt_t *rbt = NULL;
900 isc_uint64_t crc;
901 unsigned int host_big_endian;
902
903 REQUIRE(originp == NULL || *originp == NULL);
904 REQUIRE(rbtp != NULL && *rbtp == NULL);
905
906 isc_crc64_init(&crc);
907
908 CHECK(dns_rbt_create(mctx, deleter, deleter_arg, &rbt));
909
910 rbt->mmap_location = base_address;
911
912 header = (file_header_t *)((char *)base_address + header_offset);
913 if (!match_header_version(header)) {
914 result = ISC_R_INVALIDFILE;
915 goto cleanup;
916 }
917
918 #ifdef DNS_RDATASET_FIXED
919 if (header->rdataset_fixed != 1) {
920 result = ISC_R_INVALIDFILE;
921 goto cleanup;
922 }
923
924 #else
925 if (header->rdataset_fixed != 0) {
926 result = ISC_R_INVALIDFILE;
927 goto cleanup;
928 }
929 #endif
930
931 if (header->ptrsize != (isc_uint32_t) sizeof(void *)) {
932 result = ISC_R_INVALIDFILE;
933 goto cleanup;
934 }
935
936 host_big_endian = (1 == htonl(1));
937 if (header->bigendian != host_big_endian) {
938 result = ISC_R_INVALIDFILE;
939 goto cleanup;
940 }
941
942 /* Copy other data items from the header into our rbt. */
943 rbt->root = (dns_rbtnode_t *)((char *)base_address +
944 header_offset + header->first_node_offset);
945
946 if ((header->nodecount * sizeof(dns_rbtnode_t)) > filesize) {
947 result = ISC_R_INVALIDFILE;
948 goto cleanup;
949 }
950 rehash(rbt, header->nodecount);
951
952 CHECK(treefix(rbt, base_address, filesize, rbt->root,
953 dns_rootname, datafixer, fixer_arg, &crc));
954
955 isc_crc64_final(&crc);
956 #ifdef DEBUG
957 hexdump("deserializing CRC", &crc, sizeof(crc));
958 #endif
959
960 /* Check file hash */
961 if (header->crc != crc) {
962 result = ISC_R_INVALIDFILE;
963 goto cleanup;
964 }
965
966 if (header->nodecount != rbt->nodecount) {
967 result = ISC_R_INVALIDFILE;
968 goto cleanup;
969 }
970
971 #ifdef DNS_RBT_USEHASH
972 fixup_uppernodes(rbt);
973 #endif /* DNS_RBT_USEHASH */
974
975 *rbtp = rbt;
976 if (originp != NULL)
977 *originp = rbt->root;
978
979 cleanup:
980 if (result != ISC_R_SUCCESS && rbt != NULL) {
981 rbt->root = NULL;
982 rbt->nodecount = 0;
983 dns_rbt_destroy(&rbt);
984 }
985
986 return (result);
987 }
988
989 /*
990 * Initialize a red/black tree of trees.
991 */
992 isc_result_t
993 dns_rbt_create(isc_mem_t *mctx, dns_rbtdeleter_t deleter,
994 void *deleter_arg, dns_rbt_t **rbtp)
995 {
996 #ifdef DNS_RBT_USEHASH
997 isc_result_t result;
998 #endif
999 dns_rbt_t *rbt;
1000
1001 REQUIRE(mctx != NULL);
1002 REQUIRE(rbtp != NULL && *rbtp == NULL);
1003 REQUIRE(deleter == NULL ? deleter_arg == NULL : 1);
1004
1005 rbt = (dns_rbt_t *)isc_mem_get(mctx, sizeof(*rbt));
1006 if (rbt == NULL)
1007 return (ISC_R_NOMEMORY);
1008
1009 rbt->mctx = NULL;
1010 isc_mem_attach(mctx, &rbt->mctx);
1011 rbt->data_deleter = deleter;
1012 rbt->deleter_arg = deleter_arg;
1013 rbt->root = NULL;
1014 rbt->nodecount = 0;
1015 rbt->hashtable = NULL;
1016 rbt->hashsize = 0;
1017 rbt->mmap_location = NULL;
1018
1019 #ifdef DNS_RBT_USEHASH
1020 result = inithash(rbt);
1021 if (result != ISC_R_SUCCESS) {
1022 isc_mem_putanddetach(&rbt->mctx, rbt, sizeof(*rbt));
1023 return (result);
1024 }
1025 #endif
1026
1027 rbt->magic = RBT_MAGIC;
1028
1029 *rbtp = rbt;
1030
1031 return (ISC_R_SUCCESS);
1032 }
1033
1034 /*
1035 * Deallocate a red/black tree of trees.
1036 */
1037 void
1038 dns_rbt_destroy(dns_rbt_t **rbtp) {
1039 RUNTIME_CHECK(dns_rbt_destroy2(rbtp, 0) == ISC_R_SUCCESS);
1040 }
1041
1042 isc_result_t
1043 dns_rbt_destroy2(dns_rbt_t **rbtp, unsigned int quantum) {
1044 dns_rbt_t *rbt;
1045
1046 REQUIRE(rbtp != NULL && VALID_RBT(*rbtp));
1047
1048 rbt = *rbtp;
1049
1050 deletetreeflat(rbt, quantum, ISC_FALSE, &rbt->root);
1051 if (rbt->root != NULL)
1052 return (ISC_R_QUOTA);
1053
1054 INSIST(rbt->nodecount == 0);
1055
1056 rbt->mmap_location = NULL;
1057
1058 if (rbt->hashtable != NULL)
1059 isc_mem_put(rbt->mctx, rbt->hashtable,
1060 rbt->hashsize * sizeof(dns_rbtnode_t *));
1061
1062 rbt->magic = 0;
1063
1064 isc_mem_putanddetach(&rbt->mctx, rbt, sizeof(*rbt));
1065 *rbtp = NULL;
1066 return (ISC_R_SUCCESS);
1067 }
1068
1069 unsigned int
1070 dns_rbt_nodecount(dns_rbt_t *rbt) {
1071
1072 REQUIRE(VALID_RBT(rbt));
1073
1074 return (rbt->nodecount);
1075 }
1076
1077 size_t
1078 dns_rbt_hashsize(dns_rbt_t *rbt) {
1079
1080 REQUIRE(VALID_RBT(rbt));
1081
1082 return (rbt->hashsize);
1083 }
1084
1085 static inline isc_result_t
1086 chain_name(dns_rbtnodechain_t *chain, dns_name_t *name,
1087 isc_boolean_t include_chain_end)
1088 {
1089 dns_name_t nodename;
1090 isc_result_t result = ISC_R_SUCCESS;
1091 int i;
1092
1093 dns_name_init(&nodename, NULL);
1094
1095 if (include_chain_end && chain->end != NULL) {
1096 NODENAME(chain->end, &nodename);
1097 result = dns_name_copy(&nodename, name, NULL);
1098 if (result != ISC_R_SUCCESS)
1099 return (result);
1100 } else
1101 dns_name_reset(name);
1102
1103 for (i = (int)chain->level_count - 1; i >= 0; i--) {
1104 NODENAME(chain->levels[i], &nodename);
1105 result = dns_name_concatenate(name, &nodename, name, NULL);
1106
1107 if (result != ISC_R_SUCCESS)
1108 return (result);
1109 }
1110 return (result);
1111 }
1112
1113 static inline isc_result_t
1114 move_chain_to_last(dns_rbtnodechain_t *chain, dns_rbtnode_t *node) {
1115 do {
1116 /*
1117 * Go as far right and then down as much as possible,
1118 * as long as the rightmost node has a down pointer.
1119 */
1120 while (RIGHT(node) != NULL)
1121 node = RIGHT(node);
1122
1123 if (DOWN(node) == NULL)
1124 break;
1125
1126 ADD_LEVEL(chain, node);
1127 node = DOWN(node);
1128 } while (1);
1129
1130 chain->end = node;
1131
1132 return (ISC_R_SUCCESS);
1133 }
1134
1135 /*
1136 * Add 'name' to tree, initializing its data pointer with 'data'.
1137 */
1138
1139 isc_result_t
1140 dns_rbt_addnode(dns_rbt_t *rbt, const dns_name_t *name, dns_rbtnode_t **nodep) {
1141 /*
1142 * Does this thing have too many variables or what?
1143 */
1144 dns_rbtnode_t **root, *parent, *child, *current, *new_current;
1145 dns_name_t *add_name, *new_name, current_name, *prefix, *suffix;
1146 dns_fixedname_t fixedcopy, fixedprefix, fixedsuffix, fnewname;
1147 dns_offsets_t current_offsets;
1148 dns_namereln_t compared;
1149 isc_result_t result = ISC_R_SUCCESS;
1150 unsigned int level_count;
1151 unsigned int common_labels;
1152 unsigned int nlabels, hlabels;
1153 int order;
1154
1155 REQUIRE(VALID_RBT(rbt));
1156 REQUIRE(dns_name_isabsolute(name));
1157 REQUIRE(nodep != NULL && *nodep == NULL);
1158
1159 /*
1160 * Dear future BIND developer,
1161 *
1162 * After you have tried attempting to optimize this routine by
1163 * using the hashtable and have realized your folly, please
1164 * append another cross ("X") below as a warning to the next
1165 * future BIND developer:
1166 *
1167 * Number of victim developers: X
1168 *
1169 * I wish the past developer had included such a notice.
1170 *
1171 * Long form: Unlike dns_rbt_findnode(), this function does not
1172 * lend itself to be optimized using the hashtable:
1173 *
1174 * 1. In the subtree where the insertion occurs, this function
1175 * needs to have the insertion point and the order where the
1176 * lookup terminated (i.e., at the insertion point where left or
1177 * right child is NULL). This cannot be determined from the
1178 * hashtable, so at least in that subtree, a BST O(log N) lookup
1179 * is necessary.
1180 *
1181 * 2. Our RBT nodes contain not only single labels but label
1182 * sequences to optimize space usage. So at every level, we have
1183 * to look for a match in the hashtable for all superdomains in
1184 * the rest of the name we're searching. This is an O(N)
1185 * operation at least, here N being the label size of name, each
1186 * of which is a hashtable lookup involving dns_name_equal()
1187 * comparisons.
1188 */
1189
1190 /*
1191 * Create a copy of the name so the original name structure is
1192 * not modified.
1193 */
1194 add_name = dns_fixedname_initname(&fixedcopy);
1195 dns_name_clone(name, add_name);
1196
1197 if (ISC_UNLIKELY(rbt->root == NULL)) {
1198 result = create_node(rbt->mctx, add_name, &new_current);
1199 if (result == ISC_R_SUCCESS) {
1200 rbt->nodecount++;
1201 new_current->is_root = 1;
1202 #ifdef DNS_RBT_USEHASH
1203 UPPERNODE(new_current) = NULL;
1204 #endif /* DNS_RBT_USEHASH */
1205 rbt->root = new_current;
1206 *nodep = new_current;
1207 hash_node(rbt, new_current, name);
1208 }
1209 return (result);
1210 }
1211
1212 level_count = 0;
1213
1214 prefix = dns_fixedname_initname(&fixedprefix);
1215 suffix = dns_fixedname_initname(&fixedsuffix);
1216
1217 root = &rbt->root;
1218 INSIST(IS_ROOT(*root));
1219 parent = NULL;
1220 current = NULL;
1221 child = *root;
1222 dns_name_init(¤t_name, current_offsets);
1223 new_name = dns_fixedname_initname(&fnewname);
1224 nlabels = dns_name_countlabels(name);
1225 hlabels = 0;
1226
1227 do {
1228 current = child;
1229
1230 NODENAME(current, ¤t_name);
1231 compared = dns_name_fullcompare(add_name, ¤t_name,
1232 &order, &common_labels);
1233
1234 if (compared == dns_namereln_equal) {
1235 *nodep = current;
1236 result = ISC_R_EXISTS;
1237 break;
1238
1239 }
1240
1241 if (compared == dns_namereln_none) {
1242
1243 if (order < 0) {
1244 parent = current;
1245 child = LEFT(current);
1246
1247 } else if (order > 0) {
1248 parent = current;
1249 child = RIGHT(current);
1250
1251 }
1252
1253 } else {
1254 /*
1255 * This name has some suffix in common with the
1256 * name at the current node. If the name at
1257 * the current node is shorter, that means the
1258 * new name should be in a subtree. If the
1259 * name at the current node is longer, that means
1260 * the down pointer to this tree should point
1261 * to a new tree that has the common suffix, and
1262 * the non-common parts of these two names should
1263 * start a new tree.
1264 */
1265 hlabels += common_labels;
1266 if (compared == dns_namereln_subdomain) {
1267 /*
1268 * All of the existing labels are in common,
1269 * so the new name is in a subtree.
1270 * Whack off the common labels for the
1271 * not-in-common part to be searched for
1272 * in the next level.
1273 */
1274 dns_name_split(add_name, common_labels,
1275 add_name, NULL);
1276
1277 /*
1278 * Follow the down pointer (possibly NULL).
1279 */
1280 root = &DOWN(current);
1281
1282 INSIST(*root == NULL ||
1283 (IS_ROOT(*root) &&
1284 PARENT(*root) == current));
1285
1286 parent = NULL;
1287 child = DOWN(current);
1288
1289 INSIST(level_count < DNS_RBT_LEVELBLOCK);
1290 level_count++;
1291 } else {
1292 /*
1293 * The number of labels in common is fewer
1294 * than the number of labels at the current
1295 * node, so the current node must be adjusted
1296 * to have just the common suffix, and a down
1297 * pointer made to a new tree.
1298 */
1299
1300 INSIST(compared == dns_namereln_commonancestor
1301 || compared == dns_namereln_contains);
1302
1303 /*
1304 * Ensure the number of levels in the tree
1305 * does not exceed the number of logical
1306 * levels allowed by DNSSEC.
1307 *
1308 * XXXDCL need a better error result?
1309 */
1310 if (level_count >= DNS_RBT_LEVELBLOCK) {
1311 result = ISC_R_NOSPACE;
1312 break;
1313 }
1314
1315 /*
1316 * Split the name into two parts, a prefix
1317 * which is the not-in-common parts of the
1318 * two names and a suffix that is the common
1319 * parts of them.
1320 */
1321 dns_name_split(¤t_name, common_labels,
1322 prefix, suffix);
1323 result = create_node(rbt->mctx, suffix,
1324 &new_current);
1325
1326 if (result != ISC_R_SUCCESS)
1327 break;
1328
1329 /*
1330 * Reproduce the tree attributes of the
1331 * current node.
1332 */
1333 new_current->is_root = current->is_root;
1334 if (current->nsec == DNS_RBT_NSEC_HAS_NSEC)
1335 new_current->nsec = DNS_RBT_NSEC_NORMAL;
1336 else
1337 new_current->nsec = current->nsec;
1338 PARENT(new_current) = PARENT(current);
1339 LEFT(new_current) = LEFT(current);
1340 RIGHT(new_current) = RIGHT(current);
1341 COLOR(new_current) = COLOR(current);
1342
1343 /*
1344 * Fix pointers that were to the current node.
1345 */
1346 if (parent != NULL) {
1347 if (LEFT(parent) == current)
1348 LEFT(parent) = new_current;
1349 else
1350 RIGHT(parent) = new_current;
1351 }
1352 if (LEFT(new_current) != NULL)
1353 PARENT(LEFT(new_current)) =
1354 new_current;
1355 if (RIGHT(new_current) != NULL)
1356 PARENT(RIGHT(new_current)) =
1357 new_current;
1358 if (*root == current)
1359 *root = new_current;
1360
1361 NAMELEN(current) = prefix->length;
1362 OFFSETLEN(current) = prefix->labels;
1363
1364 /*
1365 * Set up the new root of the next level.
1366 * By definition it will not be the top
1367 * level tree, so clear DNS_NAMEATTR_ABSOLUTE.
1368 */
1369 current->is_root = 1;
1370 PARENT(current) = new_current;
1371 DOWN(new_current) = current;
1372 root = &DOWN(new_current);
1373 #ifdef DNS_RBT_USEHASH
1374 UPPERNODE(new_current) = UPPERNODE(current);
1375 UPPERNODE(current) = new_current;
1376 #endif /* DNS_RBT_USEHASH */
1377
1378 INSIST(level_count < DNS_RBT_LEVELBLOCK);
1379 level_count++;
1380
1381 LEFT(current) = NULL;
1382 RIGHT(current) = NULL;
1383
1384 MAKE_BLACK(current);
1385 ATTRS(current) &= ~DNS_NAMEATTR_ABSOLUTE;
1386
1387 rbt->nodecount++;
1388 dns_name_getlabelsequence(name,
1389 nlabels - hlabels,
1390 hlabels, new_name);
1391 hash_node(rbt, new_current, new_name);
1392
1393 if (common_labels ==
1394 dns_name_countlabels(add_name)) {
1395 /*
1396 * The name has been added by pushing
1397 * the not-in-common parts down to
1398 * a new level.
1399 */
1400 *nodep = new_current;
1401 return (ISC_R_SUCCESS);
1402
1403 } else {
1404 /*
1405 * The current node has no data,
1406 * because it is just a placeholder.
1407 * Its data pointer is already NULL
1408 * from create_node()), so there's
1409 * nothing more to do to it.
1410 */
1411
1412 /*
1413 * The not-in-common parts of the new
1414 * name will be inserted into the new
1415 * level following this loop (unless
1416 * result != ISC_R_SUCCESS, which
1417 * is tested after the loop ends).
1418 */
1419 dns_name_split(add_name, common_labels,
1420 add_name, NULL);
1421
1422 break;
1423 }
1424
1425 }
1426
1427 }
1428
1429 } while (ISC_LIKELY(child != NULL));
1430
1431 if (ISC_LIKELY(result == ISC_R_SUCCESS))
1432 result = create_node(rbt->mctx, add_name, &new_current);
1433
1434 if (ISC_LIKELY(result == ISC_R_SUCCESS)) {
1435 #ifdef DNS_RBT_USEHASH
1436 if (*root == NULL)
1437 UPPERNODE(new_current) = current;
1438 else
1439 UPPERNODE(new_current) = PARENT(*root);
1440 #endif /* DNS_RBT_USEHASH */
1441 addonlevel(new_current, current, order, root);
1442 rbt->nodecount++;
1443 *nodep = new_current;
1444 hash_node(rbt, new_current, name);
1445 }
1446
1447 return (result);
1448 }
1449
1450 /*
1451 * Add a name to the tree of trees, associating it with some data.
1452 */
1453 isc_result_t
1454 dns_rbt_addname(dns_rbt_t *rbt, const dns_name_t *name, void *data) {
1455 isc_result_t result;
1456 dns_rbtnode_t *node;
1457
1458 REQUIRE(VALID_RBT(rbt));
1459 REQUIRE(dns_name_isabsolute(name));
1460
1461 node = NULL;
1462
1463 result = dns_rbt_addnode(rbt, name, &node);
1464
1465 /*
1466 * dns_rbt_addnode will report the node exists even when
1467 * it does not have data associated with it, but the
1468 * dns_rbt_*name functions all behave depending on whether
1469 * there is data associated with a node.
1470 */
1471 if (result == ISC_R_SUCCESS ||
1472 (result == ISC_R_EXISTS && DATA(node) == NULL)) {
1473 DATA(node) = data;
1474 result = ISC_R_SUCCESS;
1475 }
1476
1477 return (result);
1478 }
1479
1480 /*
1481 * Find the node for "name" in the tree of trees.
1482 */
1483 isc_result_t
1484 dns_rbt_findnode(dns_rbt_t *rbt, const dns_name_t *name, dns_name_t *foundname,
1485 dns_rbtnode_t **node, dns_rbtnodechain_t *chain,
1486 unsigned int options, dns_rbtfindcallback_t callback,
1487 void *callback_arg)
1488 {
1489 dns_rbtnode_t *current, *last_compared;
1490 dns_rbtnodechain_t localchain;
1491 dns_name_t *search_name, current_name, *callback_name;
1492 dns_fixedname_t fixedcallbackname, fixedsearchname;
1493 dns_namereln_t compared;
1494 isc_result_t result, saved_result;
1495 unsigned int common_labels;
1496 unsigned int hlabels = 0;
1497 int order;
1498
1499 REQUIRE(VALID_RBT(rbt));
1500 REQUIRE(dns_name_isabsolute(name));
1501 REQUIRE(node != NULL && *node == NULL);
1502 REQUIRE((options & (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR))
1503 != (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR));
1504
1505 /*
1506 * If there is a chain it needs to appear to be in a sane state,
1507 * otherwise a chain is still needed to generate foundname and
1508 * callback_name.
1509 */
1510 if (chain == NULL) {
1511 options |= DNS_RBTFIND_NOPREDECESSOR;
1512 chain = &localchain;
1513 dns_rbtnodechain_init(chain, rbt->mctx);
1514 } else
1515 dns_rbtnodechain_reset(chain);
1516
1517 if (ISC_UNLIKELY(rbt->root == NULL))
1518 return (ISC_R_NOTFOUND);
1519
1520 /*
1521 * Appease GCC about variables it incorrectly thinks are
1522 * possibly used uninitialized.
1523 */
1524 compared = dns_namereln_none;
1525 last_compared = NULL;
1526 order = 0;
1527
1528 callback_name = dns_fixedname_initname(&fixedcallbackname);
1529
1530 /*
1531 * search_name is the name segment being sought in each tree level.
1532 * By using a fixedname, the search_name will definitely have offsets
1533 * for use by any splitting.
1534 * By using dns_name_clone, no name data should be copied thanks to
1535 * the lack of bitstring labels.
1536 */
1537 search_name = dns_fixedname_initname(&fixedsearchname);
1538 dns_name_clone(name, search_name);
1539
1540 dns_name_init(¤t_name, NULL);
1541
1542 saved_result = ISC_R_SUCCESS;
1543 current = rbt->root;
1544
1545 while (ISC_LIKELY(current != NULL)) {
1546 NODENAME(current, ¤t_name);
1547 compared = dns_name_fullcompare(search_name, ¤t_name,
1548 &order, &common_labels);
1549 /*
1550 * last_compared is used as a shortcut to start (or
1551 * continue rather) finding the stop-node of the search
1552 * when hashing was used (see much below in this
1553 * function).
1554 */
1555 last_compared = current;
1556
1557 if (compared == dns_namereln_equal)
1558 break;
1559
1560 if (compared == dns_namereln_none) {
1561 #ifdef DNS_RBT_USEHASH
1562 /*
1563 * Here, current is pointing at a subtree root
1564 * node. We try to find a matching node using
1565 * the hashtable. We can get one of 3 results
1566 * here: (a) we locate the matching node, (b) we
1567 * find a node to which the current node has a
1568 * subdomain relation, (c) we fail to find (a)
1569 * or (b).
1570 */
1571
1572 dns_name_t hash_name;
1573 dns_rbtnode_t *hnode;
1574 dns_rbtnode_t *up_current;
1575 unsigned int nlabels;
1576 unsigned int tlabels = 1;
1577 unsigned int hash;
1578
1579 /*
1580 * The case of current not being a subtree root,
1581 * that means a left or right pointer was
1582 * followed, only happens when the algorithm
1583 * fell through to the traditional binary search
1584 * because of a bitstring label. Since we
1585 * dropped the bitstring support, this should
1586 * not happen.
1587 */
1588 INSIST(IS_ROOT(current));
1589
1590 nlabels = dns_name_countlabels(search_name);
1591
1592 /*
1593 * current is the root of the current level, so
1594 * its parent is the same as its "up" pointer.
1595 */
1596 up_current = PARENT(current);
1597 dns_name_init(&hash_name, NULL);
1598
1599 hashagain:
1600 /*
1601 * Compute the hash over the full absolute
1602 * name. Look for the smallest suffix match at
1603 * this tree level (hlevel), and then at every
1604 * iteration, look for the next smallest suffix
1605 * match (add another subdomain label to the
1606 * absolute name being hashed).
1607 */
1608 dns_name_getlabelsequence(name,
1609 nlabels - tlabels,
1610 hlabels + tlabels,
1611 &hash_name);
1612 hash = dns_name_fullhash(&hash_name, ISC_FALSE);
1613 dns_name_getlabelsequence(search_name,
1614 nlabels - tlabels,
1615 tlabels, &hash_name);
1616
1617 /*
1618 * Walk all the nodes in the hash bucket pointed
1619 * by the computed hash value.
1620 */
1621 for (hnode = rbt->hashtable[hash % rbt->hashsize];
1622 hnode != NULL;
1623 hnode = hnode->hashnext)
1624 {
1625 dns_name_t hnode_name;
1626
1627 if (ISC_LIKELY(hash != HASHVAL(hnode)))
1628 continue;
1629 /*
1630 * This checks that the hashed label
1631 * sequence being looked up is at the
1632 * same tree level, so that we don't
1633 * match a labelsequence from some other
1634 * subdomain.
1635 */
1636 if (ISC_LIKELY(get_upper_node(hnode) != up_current))
1637 continue;
1638
1639 dns_name_init(&hnode_name, NULL);
1640 NODENAME(hnode, &hnode_name);
1641 if (ISC_LIKELY(dns_name_equal(&hnode_name, &hash_name)))
1642 break;
1643 }
1644
1645 if (hnode != NULL) {
1646 current = hnode;
1647 /*
1648 * This is an optimization. If hashing found
1649 * the right node, the next call to
1650 * dns_name_fullcompare() would obviously
1651 * return _equal or _subdomain. Determine
1652 * which of those would be the case by
1653 * checking if the full name was hashed. Then
1654 * make it look like dns_name_fullcompare
1655 * was called and jump to the right place.
1656 */
1657 if (tlabels == nlabels) {
1658 compared = dns_namereln_equal;
1659 break;
1660 } else {
1661 common_labels = tlabels;
1662 compared = dns_namereln_subdomain;
1663 goto subdomain;
1664 }
1665 }
1666
1667 if (tlabels++ < nlabels)
1668 goto hashagain;
1669
1670 /*
1671 * All of the labels have been tried against the hash
1672 * table. Since we dropped the support of bitstring
1673 * labels, the name isn't in the table.
1674 */
1675 current = NULL;
1676 continue;
1677
1678 #else /* DNS_RBT_USEHASH */
1679
1680 /*
1681 * Standard binary search tree movement.
1682 */
1683 if (order < 0)
1684 current = LEFT(current);
1685 else
1686 current = RIGHT(current);
1687
1688 #endif /* DNS_RBT_USEHASH */
1689
1690 } else {
1691 /*
1692 * The names have some common suffix labels.
1693 *
1694 * If the number in common are equal in length to
1695 * the current node's name length, then follow the
1696 * down pointer and search in the new tree.
1697 */
1698 if (compared == dns_namereln_subdomain) {
1699 #ifdef DNS_RBT_USEHASH
1700 subdomain:
1701 #endif
1702 /*
1703 * Whack off the current node's common parts
1704 * for the name to search in the next level.
1705 */
1706 dns_name_split(search_name, common_labels,
1707 search_name, NULL);
1708 hlabels += common_labels;
1709 /*
1710 * This might be the closest enclosing name.
1711 */
1712 if (DATA(current) != NULL ||
1713 (options & DNS_RBTFIND_EMPTYDATA) != 0)
1714 *node = current;
1715
1716 /*
1717 * Point the chain to the next level. This
1718 * needs to be done before 'current' is pointed
1719 * there because the callback in the next
1720 * block of code needs the current 'current',
1721 * but in the event the callback requests that
1722 * the search be stopped then the
1723 * DNS_R_PARTIALMATCH code at the end of this
1724 * function needs the chain pointed to the
1725 * next level.
1726 */
1727 ADD_LEVEL(chain, current);
1728
1729 /*
1730 * The caller may want to interrupt the
1731 * downward search when certain special nodes
1732 * are traversed. If this is a special node,
1733 * the callback is used to learn what the
1734 * caller wants to do.
1735 */
1736 if (callback != NULL &&
1737 FINDCALLBACK(current)) {
1738 result = chain_name(chain,
1739 callback_name,
1740 ISC_FALSE);
1741 if (result != ISC_R_SUCCESS) {
1742 dns_rbtnodechain_reset(chain);
1743 return (result);
1744 }
1745
1746 result = (callback)(current,
1747 callback_name,
1748 callback_arg);
1749 if (result != DNS_R_CONTINUE) {
1750 saved_result = result;
1751 /*
1752 * Treat this node as if it
1753 * had no down pointer.
1754 */
1755 current = NULL;
1756 break;
1757 }
1758 }
1759
1760 /*
1761 * Finally, head to the next tree level.
1762 */
1763 current = DOWN(current);
1764 } else {
1765 /*
1766 * Though there are labels in common, the
1767 * entire name at this node is not common
1768 * with the search name so the search
1769 * name does not exist in the tree.
1770 */
1771 INSIST(compared == dns_namereln_commonancestor
1772 || compared == dns_namereln_contains);
1773
1774 current = NULL;
1775 }
1776 }
1777 }
1778
1779 /*
1780 * If current is not NULL, NOEXACT is not disallowing exact matches,
1781 * and either the node has data or an empty node is ok, return
1782 * ISC_R_SUCCESS to indicate an exact match.
1783 */
1784 if (current != NULL && (options & DNS_RBTFIND_NOEXACT) == 0 &&
1785 (DATA(current) != NULL ||
1786 (options & DNS_RBTFIND_EMPTYDATA) != 0)) {
1787 /*
1788 * Found an exact match.
1789 */
1790 chain->end = current;
1791 chain->level_matches = chain->level_count;
1792
1793 if (foundname != NULL)
1794 result = chain_name(chain, foundname, ISC_TRUE);
1795 else
1796 result = ISC_R_SUCCESS;
1797
1798 if (result == ISC_R_SUCCESS) {
1799 *node = current;
1800 result = saved_result;
1801 } else
1802 *node = NULL;
1803 } else {
1804 /*
1805 * Did not find an exact match (or did not want one).
1806 */
1807 if (*node != NULL) {
1808 /*
1809 * ... but found a partially matching superdomain.
1810 * Unwind the chain to the partial match node
1811 * to set level_matches to the level above the node,
1812 * and then to derive the name.
1813 *
1814 * chain->level_count is guaranteed to be at least 1
1815 * here because by definition of finding a superdomain,
1816 * the chain is pointed to at least the first subtree.
1817 */
1818 chain->level_matches = chain->level_count - 1;
1819
1820 while (chain->levels[chain->level_matches] != *node) {
1821 INSIST(chain->level_matches > 0);
1822 chain->level_matches--;
1823 }
1824
1825 if (foundname != NULL) {
1826 unsigned int saved_count = chain->level_count;
1827
1828 chain->level_count = chain->level_matches + 1;
1829
1830 result = chain_name(chain, foundname,
1831 ISC_FALSE);
1832
1833 chain->level_count = saved_count;
1834 } else
1835 result = ISC_R_SUCCESS;
1836
1837 if (result == ISC_R_SUCCESS)
1838 result = DNS_R_PARTIALMATCH;
1839
1840 } else
1841 result = ISC_R_NOTFOUND;
1842
1843 if (current != NULL) {
1844 /*
1845 * There was an exact match but either
1846 * DNS_RBTFIND_NOEXACT was set, or
1847 * DNS_RBTFIND_EMPTYDATA was set and the node had no
1848 * data. A policy decision was made to set the
1849 * chain to the exact match, but this is subject
1850 * to change if it becomes apparent that something
1851 * else would be more useful. It is important that
1852 * this case is handled here, because the predecessor
1853 * setting code below assumes the match was not exact.
1854 */
1855 INSIST(((options & DNS_RBTFIND_NOEXACT) != 0) ||
1856 ((options & DNS_RBTFIND_EMPTYDATA) == 0 &&
1857 DATA(current) == NULL));
1858 chain->end = current;
1859
1860 } else if ((options & DNS_RBTFIND_NOPREDECESSOR) != 0) {
1861 /*
1862 * Ensure the chain points nowhere.
1863 */
1864 chain->end = NULL;
1865
1866 } else {
1867 /*
1868 * Since there was no exact match, the chain argument
1869 * needs to be pointed at the DNSSEC predecessor of
1870 * the search name.
1871 */
1872 if (compared == dns_namereln_subdomain) {
1873 /*
1874 * Attempted to follow a down pointer that was
1875 * NULL, which means the searched for name was
1876 * a subdomain of a terminal name in the tree.
1877 * Since there are no existing subdomains to
1878 * order against, the terminal name is the
1879 * predecessor.
1880 */
1881 INSIST(chain->level_count > 0);
1882 INSIST(chain->level_matches <
1883 chain->level_count);
1884 chain->end =
1885 chain->levels[--chain->level_count];
1886
1887 } else {
1888 isc_result_t result2;
1889
1890 /*
1891 * Point current to the node that stopped
1892 * the search.
1893 *
1894 * With the hashing modification that has been
1895 * added to the algorithm, the stop node of a
1896 * standard binary search is not known. So it
1897 * has to be found. There is probably a more
1898 * clever way of doing this.
1899 *
1900 * The assignment of current to NULL when
1901 * the relationship is *not* dns_namereln_none,
1902 * even though it later gets set to the same
1903 * last_compared anyway, is simply to not push
1904 * the while loop in one more level of
1905 * indentation.
1906 */
1907 if (compared == dns_namereln_none)
1908 current = last_compared;
1909 else
1910 current = NULL;
1911
1912 while (current != NULL) {
1913 NODENAME(current, ¤t_name);
1914 compared = dns_name_fullcompare(
1915 search_name,
1916 ¤t_name,
1917 &order,
1918 &common_labels);
1919 POST(compared);
1920
1921 last_compared = current;
1922
1923 /*
1924 * Standard binary search movement.
1925 */
1926 if (order < 0)
1927 current = LEFT(current);
1928 else
1929 current = RIGHT(current);
1930
1931 }
1932
1933 current = last_compared;
1934
1935 /*
1936 * Reached a point within a level tree that
1937 * positively indicates the name is not
1938 * present, but the stop node could be either
1939 * less than the desired name (order > 0) or
1940 * greater than the desired name (order < 0).
1941 *
1942 * If the stop node is less, it is not
1943 * necessarily the predecessor. If the stop
1944 * node has a down pointer, then the real
1945 * predecessor is at the end of a level below
1946 * (not necessarily the next level).
1947 * Move down levels until the rightmost node
1948 * does not have a down pointer.
1949 *
1950 * When the stop node is greater, it is
1951 * the successor. All the logic for finding
1952 * the predecessor is handily encapsulated
1953 * in dns_rbtnodechain_prev. In the event
1954 * that the search name is less than anything
1955 * else in the tree, the chain is reset.
1956 * XXX DCL What is the best way for the caller
1957 * to know that the search name has
1958 * no predecessor?
1959 */
1960
1961
1962 if (order > 0) {
1963 if (DOWN(current) != NULL) {
1964 ADD_LEVEL(chain, current);
1965
1966 result2 =
1967 move_chain_to_last(chain,
1968 DOWN(current));
1969
1970 if (result2 != ISC_R_SUCCESS)
1971 result = result2;
1972 } else
1973 /*
1974 * Ah, the pure and simple
1975 * case. The stop node is the
1976 * predecessor.
1977 */
1978 chain->end = current;
1979
1980 } else {
1981 INSIST(order < 0);
1982
1983 chain->end = current;
1984
1985 result2 = dns_rbtnodechain_prev(chain,
1986 NULL,
1987 NULL);
1988 if (result2 == ISC_R_SUCCESS ||
1989 result2 == DNS_R_NEWORIGIN)
1990 ; /* Nothing. */
1991 else if (result2 == ISC_R_NOMORE)
1992 /*
1993 * There is no predecessor.
1994 */
1995 dns_rbtnodechain_reset(chain);
1996 else
1997 result = result2;
1998 }
1999
2000 }
2001 }
2002 }
2003
2004 ENSURE(*node == NULL || DNS_RBTNODE_VALID(*node));
2005
2006 return (result);
2007 }
2008
2009 /*
2010 * Get the data pointer associated with 'name'.
2011 */
2012 isc_result_t
2013 dns_rbt_findname(dns_rbt_t *rbt, const dns_name_t *name, unsigned int options,
2014 dns_name_t *foundname, void **data) {
2015 dns_rbtnode_t *node = NULL;
2016 isc_result_t result;
2017
2018 REQUIRE(data != NULL && *data == NULL);
2019
2020 result = dns_rbt_findnode(rbt, name, foundname, &node, NULL,
2021 options, NULL, NULL);
2022
2023 if (node != NULL &&
2024 (DATA(node) != NULL || (options & DNS_RBTFIND_EMPTYDATA) != 0))
2025 *data = DATA(node);
2026 else
2027 result = ISC_R_NOTFOUND;
2028
2029 return (result);
2030 }
2031
2032 /*
2033 * Delete a name from the tree of trees.
2034 */
2035 isc_result_t
2036 dns_rbt_deletename(dns_rbt_t *rbt, const dns_name_t *name,
2037 isc_boolean_t recurse)
2038 {
2039 dns_rbtnode_t *node = NULL;
2040 isc_result_t result;
2041
2042 REQUIRE(VALID_RBT(rbt));
2043 REQUIRE(dns_name_isabsolute(name));
2044
2045 /*
2046 * First, find the node.
2047 *
2048 * When searching, the name might not have an exact match:
2049 * consider a.b.a.com, b.b.a.com and c.b.a.com as the only
2050 * elements of a tree, which would make layer 1 a single
2051 * node tree of "b.a.com" and layer 2 a three node tree of
2052 * a, b, and c. Deleting a.com would find only a partial depth
2053 * match in the first layer. Should it be a requirement that
2054 * that the name to be deleted have data? For now, it is.
2055 *
2056 * ->dirty, ->locknum and ->references are ignored; they are
2057 * solely the province of rbtdb.c.
2058 */
2059 result = dns_rbt_findnode(rbt, name, NULL, &node, NULL,
2060 DNS_RBTFIND_NOOPTIONS, NULL, NULL);
2061
2062 if (result == ISC_R_SUCCESS) {
2063 if (DATA(node) != NULL)
2064 result = dns_rbt_deletenode(rbt, node, recurse);
2065 else
2066 result = ISC_R_NOTFOUND;
2067
2068 } else if (result == DNS_R_PARTIALMATCH)
2069 result = ISC_R_NOTFOUND;
2070
2071 return (result);
2072 }
2073
2074 /*
2075 * Remove a node from the tree of trees.
2076 *
2077 * NOTE WELL: deletion is *not* symmetric with addition; that is, reversing
2078 * a sequence of additions to be deletions will not generally get the
2079 * tree back to the state it started in. For example, if the addition
2080 * of "b.c" caused the node "a.b.c" to be split, pushing "a" to its own level,
2081 * then the subsequent deletion of "b.c" will not cause "a" to be pulled up,
2082 * restoring "a.b.c". The RBT *used* to do this kind of rejoining, but it
2083 * turned out to be a bad idea because it could corrupt an active nodechain
2084 * that had "b.c" as one of its levels -- and the RBT has no idea what
2085 * nodechains are in use by callers, so it can't even *try* to helpfully
2086 * fix them up (which would probably be doomed to failure anyway).
2087 *
2088 * Similarly, it is possible to leave the tree in a state where a supposedly
2089 * deleted node still exists. The first case of this is obvious; take
2090 * the tree which has "b.c" on one level, pointing to "a". Now deleted "b.c".
2091 * It was just established in the previous paragraph why we can't pull "a"
2092 * back up to its parent level. But what happens when "a" then gets deleted?
2093 * "b.c" is left hanging around without data or children. This condition
2094 * is actually pretty easy to detect, but ... should it really be removed?
2095 * Is a chain pointing to it? An iterator? Who knows! (Note that the
2096 * references structure member cannot be looked at because it is private to
2097 * rbtdb.) This is ugly and makes me unhappy, but after hours of trying to
2098 * make it more aesthetically proper and getting nowhere, this is the way it
2099 * is going to stay until such time as it proves to be a *real* problem.
2100 *
2101 * Finally, for reference, note that the original routine that did node
2102 * joining was called join_nodes(). It has been excised, living now only
2103 * in the CVS history, but comments have been left behind that point to it just
2104 * in case someone wants to muck with this some more.
2105 *
2106 * The one positive aspect of all of this is that joining used to have a
2107 * case where it might fail. Without trying to join, now this function always
2108 * succeeds. It still returns isc_result_t, though, so the API wouldn't change.
2109 */
2110 isc_result_t
2111 dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, isc_boolean_t recurse)
2112 {
2113 dns_rbtnode_t *parent;
2114
2115 REQUIRE(VALID_RBT(rbt));
2116 REQUIRE(DNS_RBTNODE_VALID(node));
2117 INSIST(rbt->nodecount != 0);
2118
2119 if (DOWN(node) != NULL) {
2120 if (recurse) {
2121 PARENT(DOWN(node)) = NULL;
2122 deletetreeflat(rbt, 0, ISC_TRUE, &DOWN(node));
2123 } else {
2124 if (DATA(node) != NULL && rbt->data_deleter != NULL)
2125 rbt->data_deleter(DATA(node), rbt->deleter_arg);
2126 DATA(node) = NULL;
2127
2128 /*
2129 * Since there is at least one node below this one and
2130 * no recursion was requested, the deletion is
2131 * complete. The down node from this node might be all
2132 * by itself on a single level, so join_nodes() could
2133 * be used to collapse the tree (with all the caveats
2134 * of the comment at the start of this function).
2135 * But join_nodes() function has now been removed.
2136 */
2137 return (ISC_R_SUCCESS);
2138 }
2139 }
2140
2141 /*
2142 * Note the node that points to the level of the node
2143 * that is being deleted. If the deleted node is the
2144 * top level, parent will be set to NULL.
2145 */
2146 parent = get_upper_node(node);
2147
2148 /*
2149 * This node now has no down pointer, so now it needs
2150 * to be removed from this level.
2151 */
2152 deletefromlevel(node, parent == NULL ? &rbt->root : &DOWN(parent));
2153
2154 if (DATA(node) != NULL && rbt->data_deleter != NULL)
2155 rbt->data_deleter(DATA(node), rbt->deleter_arg);
2156
2157 unhash_node(rbt, node);
2158 #if DNS_RBT_USEMAGIC
2159 node->magic = 0;
2160 #endif
2161 dns_rbtnode_refdestroy(node);
2162
2163 freenode(rbt, &node);
2164
2165 /*
2166 * This function never fails.
2167 */
2168 return (ISC_R_SUCCESS);
2169 }
2170
2171 void
2172 dns_rbt_namefromnode(dns_rbtnode_t *node, dns_name_t *name) {
2173
2174 REQUIRE(DNS_RBTNODE_VALID(node));
2175 REQUIRE(name != NULL);
2176 REQUIRE(name->offsets == NULL);
2177
2178 NODENAME(node, name);
2179 }
2180
2181 isc_result_t
2182 dns_rbt_fullnamefromnode(dns_rbtnode_t *node, dns_name_t *name) {
2183 dns_name_t current;
2184 isc_result_t result;
2185
2186 REQUIRE(DNS_RBTNODE_VALID(node));
2187 REQUIRE(name != NULL);
2188 REQUIRE(name->buffer != NULL);
2189
2190 dns_name_init(¤t, NULL);
2191 dns_name_reset(name);
2192
2193 do {
2194 INSIST(node != NULL);
2195
2196 NODENAME(node, ¤t);
2197
2198 result = dns_name_concatenate(name, ¤t, name, NULL);
2199 if (result != ISC_R_SUCCESS)
2200 break;
2201
2202 node = get_upper_node(node);
2203 } while (! dns_name_isabsolute(name));
2204
2205 return (result);
2206 }
2207
2208 char *
2209 dns_rbt_formatnodename(dns_rbtnode_t *node, char *printname, unsigned int size)
2210 {
2211 dns_fixedname_t fixedname;
2212 dns_name_t *name;
2213 isc_result_t result;
2214
2215 REQUIRE(DNS_RBTNODE_VALID(node));
2216 REQUIRE(printname != NULL);
2217
2218 name = dns_fixedname_initname(&fixedname);
2219 result = dns_rbt_fullnamefromnode(node, name);
2220 if (result == ISC_R_SUCCESS)
2221 dns_name_format(name, printname, size);
2222 else
2223 snprintf(printname, size, "<error building name: %s>",
2224 dns_result_totext(result));
2225
2226 return (printname);
2227 }
2228
2229 static isc_result_t
2230 create_node(isc_mem_t *mctx, const dns_name_t *name, dns_rbtnode_t **nodep) {
2231 dns_rbtnode_t *node;
2232 isc_region_t region;
2233 unsigned int labels;
2234 size_t nodelen;
2235
2236 REQUIRE(name->offsets != NULL);
2237
2238 dns_name_toregion(name, ®ion);
2239 labels = dns_name_countlabels(name);
2240 ENSURE(labels > 0);
2241
2242 /*
2243 * Allocate space for the node structure, the name, and the offsets.
2244 */
2245 nodelen = sizeof(dns_rbtnode_t) + region.length + labels + 1;
2246 node = (dns_rbtnode_t *)isc_mem_get(mctx, nodelen);
2247 if (node == NULL)
2248 return (ISC_R_NOMEMORY);
2249 memset(node, 0, nodelen);
2250
2251 node->is_root = 0;
2252 PARENT(node) = NULL;
2253 RIGHT(node) = NULL;
2254 LEFT(node) = NULL;
2255 DOWN(node) = NULL;
2256 DATA(node) = NULL;
2257 node->is_mmapped = 0;
2258 node->down_is_relative = 0;
2259 node->left_is_relative = 0;
2260 node->right_is_relative = 0;
2261 node->parent_is_relative = 0;
2262 node->data_is_relative = 0;
2263 node->rpz = 0;
2264
2265 #ifdef DNS_RBT_USEHASH
2266 HASHNEXT(node) = NULL;
2267 HASHVAL(node) = 0;
2268 #endif
2269
2270 ISC_LINK_INIT(node, deadlink);
2271
2272 LOCKNUM(node) = 0;
2273 WILD(node) = 0;
2274 DIRTY(node) = 0;
2275 dns_rbtnode_refinit(node, 0);
2276 node->find_callback = 0;
2277 node->nsec = DNS_RBT_NSEC_NORMAL;
2278
2279 MAKE_BLACK(node);
2280
2281 /*
2282 * The following is stored to make reconstructing a name from the
2283 * stored value in the node easy: the length of the name, the number
2284 * of labels, whether the name is absolute or not, the name itself,
2285 * and the name's offsets table.
2286 *
2287 * XXX RTH
2288 * The offsets table could be made smaller by eliminating the
2289 * first offset, which is always 0. This requires changes to
2290 * lib/dns/name.c.
2291 *
2292 * Note: OLDOFFSETLEN *must* be assigned *after* OLDNAMELEN is assigned
2293 * as it uses OLDNAMELEN.
2294 */
2295 OLDNAMELEN(node) = NAMELEN(node) = region.length;
2296 OLDOFFSETLEN(node) = OFFSETLEN(node) = labels;
2297 ATTRS(node) = name->attributes;
2298
2299 memmove(NAME(node), region.base, region.length);
2300 memmove(OFFSETS(node), name->offsets, labels);
2301
2302 #if DNS_RBT_USEMAGIC
2303 node->magic = DNS_RBTNODE_MAGIC;
2304 #endif
2305 *nodep = node;
2306
2307 return (ISC_R_SUCCESS);
2308 }
2309
2310 #ifdef DNS_RBT_USEHASH
2311 static inline void
2312 hash_add_node(dns_rbt_t *rbt, dns_rbtnode_t *node, const dns_name_t *name) {
2313 unsigned int hash;
2314
2315 REQUIRE(name != NULL);
2316
2317 HASHVAL(node) = dns_name_fullhash(name, ISC_FALSE);
2318
2319 hash = HASHVAL(node) % rbt->hashsize;
2320 HASHNEXT(node) = rbt->hashtable[hash];
2321
2322 rbt->hashtable[hash] = node;
2323 }
2324
2325 static isc_result_t
2326 inithash(dns_rbt_t *rbt) {
2327 unsigned int bytes;
2328
2329 rbt->hashsize = RBT_HASH_SIZE;
2330 bytes = (unsigned int)rbt->hashsize * sizeof(dns_rbtnode_t *);
2331 rbt->hashtable = isc_mem_get(rbt->mctx, bytes);
2332
2333 if (rbt->hashtable == NULL)
2334 return (ISC_R_NOMEMORY);
2335
2336 memset(rbt->hashtable, 0, bytes);
2337
2338 return (ISC_R_SUCCESS);
2339 }
2340
2341 static void
2342 rehash(dns_rbt_t *rbt, unsigned int newcount) {
2343 unsigned int oldsize;
2344 dns_rbtnode_t **oldtable;
2345 dns_rbtnode_t *node;
2346 dns_rbtnode_t *nextnode;
2347 unsigned int hash;
2348 unsigned int i;
2349
2350 oldsize = (unsigned int)rbt->hashsize;
2351 oldtable = rbt->hashtable;
2352 do {
2353 INSIST((rbt->hashsize * 2 + 1) > rbt->hashsize);
2354 rbt->hashsize = rbt->hashsize * 2 + 1;
2355 } while (newcount >= (rbt->hashsize * 3));
2356 rbt->hashtable = isc_mem_get(rbt->mctx,
2357 rbt->hashsize * sizeof(dns_rbtnode_t *));
2358 if (rbt->hashtable == NULL) {
2359 rbt->hashtable = oldtable;
2360 rbt->hashsize = oldsize;
2361 return;
2362 }
2363
2364 for (i = 0; i < rbt->hashsize; i++)
2365 rbt->hashtable[i] = NULL;
2366
2367 for (i = 0; i < oldsize; i++) {
2368 for (node = oldtable[i]; node != NULL; node = nextnode) {
2369 hash = HASHVAL(node) % rbt->hashsize;
2370 nextnode = HASHNEXT(node);
2371 HASHNEXT(node) = rbt->hashtable[hash];
2372 rbt->hashtable[hash] = node;
2373 }
2374 }
2375
2376 isc_mem_put(rbt->mctx, oldtable, oldsize * sizeof(dns_rbtnode_t *));
2377 }
2378
2379 static inline void
2380 hash_node(dns_rbt_t *rbt, dns_rbtnode_t *node, const dns_name_t *name) {
2381 REQUIRE(DNS_RBTNODE_VALID(node));
2382
2383 if (rbt->nodecount >= (rbt->hashsize * 3))
2384 rehash(rbt, rbt->nodecount);
2385
2386 hash_add_node(rbt, node, name);
2387 }
2388
2389 static inline void
2390 unhash_node(dns_rbt_t *rbt, dns_rbtnode_t *node) {
2391 unsigned int bucket;
2392 dns_rbtnode_t *bucket_node;
2393
2394 REQUIRE(DNS_RBTNODE_VALID(node));
2395
2396 bucket = HASHVAL(node) % rbt->hashsize;
2397 bucket_node = rbt->hashtable[bucket];
2398
2399 if (bucket_node == node) {
2400 rbt->hashtable[bucket] = HASHNEXT(node);
2401 } else {
2402 while (HASHNEXT(bucket_node) != node) {
2403 INSIST(HASHNEXT(bucket_node) != NULL);
2404 bucket_node = HASHNEXT(bucket_node);
2405 }
2406 HASHNEXT(bucket_node) = HASHNEXT(node);
2407 }
2408 }
2409 #endif /* DNS_RBT_USEHASH */
2410
2411 static inline void
2412 rotate_left(dns_rbtnode_t *node, dns_rbtnode_t **rootp) {
2413 dns_rbtnode_t *child;
2414
2415 REQUIRE(DNS_RBTNODE_VALID(node));
2416 REQUIRE(rootp != NULL);
2417
2418 child = RIGHT(node);
2419 INSIST(child != NULL);
2420
2421 RIGHT(node) = LEFT(child);
2422 if (LEFT(child) != NULL)
2423 PARENT(LEFT(child)) = node;
2424 LEFT(child) = node;
2425
2426 PARENT(child) = PARENT(node);
2427
2428 if (IS_ROOT(node)) {
2429 *rootp = child;
2430 child->is_root = 1;
2431 node->is_root = 0;
2432
2433 } else {
2434 if (LEFT(PARENT(node)) == node)
2435 LEFT(PARENT(node)) = child;
2436 else
2437 RIGHT(PARENT(node)) = child;
2438 }
2439
2440 PARENT(node) = child;
2441 }
2442
2443 static inline void
2444 rotate_right(dns_rbtnode_t *node, dns_rbtnode_t **rootp) {
2445 dns_rbtnode_t *child;
2446
2447 REQUIRE(DNS_RBTNODE_VALID(node));
2448 REQUIRE(rootp != NULL);
2449
2450 child = LEFT(node);
2451 INSIST(child != NULL);
2452
2453 LEFT(node) = RIGHT(child);
2454 if (RIGHT(child) != NULL)
2455 PARENT(RIGHT(child)) = node;
2456 RIGHT(child) = node;
2457
2458 PARENT(child) = PARENT(node);
2459
2460 if (IS_ROOT(node)) {
2461 *rootp = child;
2462 child->is_root = 1;
2463 node->is_root = 0;
2464
2465 } else {
2466 if (LEFT(PARENT(node)) == node)
2467 LEFT(PARENT(node)) = child;
2468 else
2469 RIGHT(PARENT(node)) = child;
2470 }
2471
2472 PARENT(node) = child;
2473 }
2474
2475 /*
2476 * This is the real workhorse of the insertion code, because it does the
2477 * true red/black tree on a single level.
2478 */
2479 static void
2480 addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order,
2481 dns_rbtnode_t **rootp)
2482 {
2483 dns_rbtnode_t *child, *root, *parent, *grandparent;
2484 dns_name_t add_name, current_name;
2485 dns_offsets_t add_offsets, current_offsets;
2486
2487 REQUIRE(rootp != NULL);
2488 REQUIRE(DNS_RBTNODE_VALID(node) && LEFT(node) == NULL &&
2489 RIGHT(node) == NULL);
2490 REQUIRE(current != NULL);
2491
2492 root = *rootp;
2493 if (root == NULL) {
2494 /*
2495 * First node of a level.
2496 */
2497 MAKE_BLACK(node);
2498 node->is_root = 1;
2499 PARENT(node) = current;
2500 *rootp = node;
2501 return;
2502 }
2503
2504 child = root;
2505 POST(child);
2506
2507 dns_name_init(&add_name, add_offsets);
2508 NODENAME(node, &add_name);
2509
2510 dns_name_init(¤t_name, current_offsets);
2511 NODENAME(current, ¤t_name);
2512
2513 if (order < 0) {
2514 INSIST(LEFT(current) == NULL);
2515 LEFT(current) = node;
2516 } else {
2517 INSIST(RIGHT(current) == NULL);
2518 RIGHT(current) = node;
2519 }
2520
2521 INSIST(PARENT(node) == NULL);
2522 PARENT(node) = current;
2523
2524 MAKE_RED(node);
2525
2526 while (node != root && IS_RED(PARENT(node))) {
2527 /*
2528 * XXXDCL could do away with separate parent and grandparent
2529 * variables. They are vestiges of the days before parent
2530 * pointers. However, they make the code a little clearer.
2531 */
2532
2533 parent = PARENT(node);
2534 grandparent = PARENT(parent);
2535
2536 if (parent == LEFT(grandparent)) {
2537 child = RIGHT(grandparent);
2538 if (child != NULL && IS_RED(child)) {
2539 MAKE_BLACK(parent);
2540 MAKE_BLACK(child);
2541 MAKE_RED(grandparent);
2542 node = grandparent;
2543 } else {
2544 if (node == RIGHT(parent)) {
2545 rotate_left(parent, &root);
2546 node = parent;
2547 parent = PARENT(node);
2548 grandparent = PARENT(parent);
2549 }
2550 MAKE_BLACK(parent);
2551 MAKE_RED(grandparent);
2552 rotate_right(grandparent, &root);
2553 }
2554 } else {
2555 child = LEFT(grandparent);
2556 if (child != NULL && IS_RED(child)) {
2557 MAKE_BLACK(parent);
2558 MAKE_BLACK(child);
2559 MAKE_RED(grandparent);
2560 node = grandparent;
2561 } else {
2562 if (node == LEFT(parent)) {
2563 rotate_right(parent, &root);
2564 node = parent;
2565 parent = PARENT(node);
2566 grandparent = PARENT(parent);
2567 }
2568 MAKE_BLACK(parent);
2569 MAKE_RED(grandparent);
2570 rotate_left(grandparent, &root);
2571 }
2572 }
2573 }
2574
2575 MAKE_BLACK(root);
2576 ENSURE(IS_ROOT(root));
2577 *rootp = root;
2578
2579 return;
2580 }
2581
2582 /*
2583 * This is the real workhorse of the deletion code, because it does the
2584 * true red/black tree on a single level.
2585 */
2586 static void
2587 deletefromlevel(dns_rbtnode_t *item, dns_rbtnode_t **rootp) {
2588 dns_rbtnode_t *child, *sibling, *parent;
2589 dns_rbtnode_t *successor;
2590
2591 REQUIRE(item != NULL);
2592
2593 /*
2594 * Verify that the parent history is (apparently) correct.
2595 */
2596 INSIST((IS_ROOT(item) && *rootp == item) ||
2597 (! IS_ROOT(item) &&
2598 (LEFT(PARENT(item)) == item ||
2599 RIGHT(PARENT(item)) == item)));
2600
2601 child = NULL;
2602
2603 if (LEFT(item) == NULL) {
2604 if (RIGHT(item) == NULL) {
2605 if (IS_ROOT(item)) {
2606 /*
2607 * This is the only item in the tree.
2608 */
2609 *rootp = NULL;
2610 return;
2611 }
2612 } else
2613 /*
2614 * This node has one child, on the right.
2615 */
2616 child = RIGHT(item);
2617
2618 } else if (RIGHT(item) == NULL)
2619 /*
2620 * This node has one child, on the left.
2621 */
2622 child = LEFT(item);
2623 else {
2624 dns_rbtnode_t holder, *tmp = &holder;
2625
2626 /*
2627 * This node has two children, so it cannot be directly
2628 * deleted. Find its immediate in-order successor and
2629 * move it to this location, then do the deletion at the
2630 * old site of the successor.
2631 */
2632 successor = RIGHT(item);
2633 while (LEFT(successor) != NULL)
2634 successor = LEFT(successor);
2635
2636 /*
2637 * The successor cannot possibly have a left child;
2638 * if there is any child, it is on the right.
2639 */
2640 if (RIGHT(successor) != NULL)
2641 child = RIGHT(successor);
2642
2643 /*
2644 * Swap the two nodes; it would be simpler to just replace
2645 * the value being deleted with that of the successor,
2646 * but this rigamarole is done so the caller has complete
2647 * control over the pointers (and memory allocation) of
2648 * all of nodes. If just the key value were removed from
2649 * the tree, the pointer to the node would be unchanged.
2650 */
2651
2652 /*
2653 * First, put the successor in the tree location of the
2654 * node to be deleted. Save its existing tree pointer
2655 * information, which will be needed when linking up
2656 * delete to the successor's old location.
2657 */
2658 memmove(tmp, successor, sizeof(dns_rbtnode_t));
2659
2660 if (IS_ROOT(item)) {
2661 *rootp = successor;
2662 successor->is_root = ISC_TRUE;
2663 item->is_root = ISC_FALSE;
2664
2665 } else
2666 if (LEFT(PARENT(item)) == item)
2667 LEFT(PARENT(item)) = successor;
2668 else
2669 RIGHT(PARENT(item)) = successor;
2670
2671 PARENT(successor) = PARENT(item);
2672 LEFT(successor) = LEFT(item);
2673 RIGHT(successor) = RIGHT(item);
2674 COLOR(successor) = COLOR(item);
2675
2676 if (LEFT(successor) != NULL)
2677 PARENT(LEFT(successor)) = successor;
2678 if (RIGHT(successor) != successor)
2679 PARENT(RIGHT(successor)) = successor;
2680
2681 /*
2682 * Now relink the node to be deleted into the
2683 * successor's previous tree location. PARENT(tmp)
2684 * is the successor's original parent.
2685 */
2686 INSIST(! IS_ROOT(item));
2687
2688 if (PARENT(tmp) == item) {
2689 /*
2690 * Node being deleted was successor's parent.
2691 */
2692 RIGHT(successor) = item;
2693 PARENT(item) = successor;
2694
2695 } else {
2696 LEFT(PARENT(tmp)) = item;
2697 PARENT(item) = PARENT(tmp);
2698 }
2699
2700 /*
2701 * Original location of successor node has no left.
2702 */
2703 LEFT(item) = NULL;
2704 RIGHT(item) = RIGHT(tmp);
2705 COLOR(item) = COLOR(tmp);
2706 }
2707
2708 /*
2709 * Remove the node by removing the links from its parent.
2710 */
2711 if (! IS_ROOT(item)) {
2712 if (LEFT(PARENT(item)) == item)
2713 LEFT(PARENT(item)) = child;
2714 else
2715 RIGHT(PARENT(item)) = child;
2716
2717 if (child != NULL)
2718 PARENT(child) = PARENT(item);
2719
2720 } else {
2721 /*
2722 * This is the root being deleted, and at this point
2723 * it is known to have just one child.
2724 */
2725 *rootp = child;
2726 child->is_root = 1;
2727 PARENT(child) = PARENT(item);
2728 }
2729
2730 /*
2731 * Fix color violations.
2732 */
2733 if (IS_BLACK(item)) {
2734 parent = PARENT(item);
2735
2736 while (child != *rootp && IS_BLACK(child)) {
2737 INSIST(child == NULL || ! IS_ROOT(child));
2738
2739 if (LEFT(parent) == child) {
2740 sibling = RIGHT(parent);
2741
2742 if (IS_RED(sibling)) {
2743 MAKE_BLACK(sibling);
2744 MAKE_RED(parent);
2745 rotate_left(parent, rootp);
2746 sibling = RIGHT(parent);
2747 }
2748
2749 INSIST(sibling != NULL);
2750
2751 if (IS_BLACK(LEFT(sibling)) &&
2752 IS_BLACK(RIGHT(sibling))) {
2753 MAKE_RED(sibling);
2754 child = parent;
2755
2756 } else {
2757
2758 if (IS_BLACK(RIGHT(sibling))) {
2759 MAKE_BLACK(LEFT(sibling));
2760 MAKE_RED(sibling);
2761 rotate_right(sibling, rootp);
2762 sibling = RIGHT(parent);
2763 }
2764
2765 COLOR(sibling) = COLOR(parent);
2766 MAKE_BLACK(parent);
2767 INSIST(RIGHT(sibling) != NULL);
2768 MAKE_BLACK(RIGHT(sibling));
2769 rotate_left(parent, rootp);
2770 child = *rootp;
2771 }
2772
2773 } else {
2774 /*
2775 * Child is parent's right child.
2776 * Everything is done the same as above,
2777 * except mirrored.
2778 */
2779 sibling = LEFT(parent);
2780
2781 if (IS_RED(sibling)) {
2782 MAKE_BLACK(sibling);
2783 MAKE_RED(parent);
2784 rotate_right(parent, rootp);
2785 sibling = LEFT(parent);
2786 }
2787
2788 INSIST(sibling != NULL);
2789
2790 if (IS_BLACK(LEFT(sibling)) &&
2791 IS_BLACK(RIGHT(sibling))) {
2792 MAKE_RED(sibling);
2793 child = parent;
2794
2795 } else {
2796 if (IS_BLACK(LEFT(sibling))) {
2797 MAKE_BLACK(RIGHT(sibling));
2798 MAKE_RED(sibling);
2799 rotate_left(sibling, rootp);
2800 sibling = LEFT(parent);
2801 }
2802
2803 COLOR(sibling) = COLOR(parent);
2804 MAKE_BLACK(parent);
2805 INSIST(LEFT(sibling) != NULL);
2806 MAKE_BLACK(LEFT(sibling));
2807 rotate_right(parent, rootp);
2808 child = *rootp;
2809 }
2810 }
2811
2812 parent = PARENT(child);
2813 }
2814
2815 if (IS_RED(child))
2816 MAKE_BLACK(child);
2817 }
2818 }
2819
2820 static void
2821 freenode(dns_rbt_t *rbt, dns_rbtnode_t **nodep) {
2822 dns_rbtnode_t *node = *nodep;
2823
2824 if (node->is_mmapped == 0) {
2825 isc_mem_put(rbt->mctx, node, NODE_SIZE(node));
2826 }
2827 *nodep = NULL;
2828
2829 rbt->nodecount--;
2830 }
2831
2832 static void
2833 deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, isc_boolean_t unhash,
2834 dns_rbtnode_t **nodep)
2835 {
2836 dns_rbtnode_t *root = *nodep;
2837
2838 while (root != NULL) {
2839 /*
2840 * If there is a left, right or down node, walk into it
2841 * and iterate.
2842 */
2843 if (LEFT(root) != NULL) {
2844 dns_rbtnode_t *node = root;
2845 root = LEFT(root);
2846 LEFT(node) = NULL;
2847 } else if (RIGHT(root) != NULL) {
2848 dns_rbtnode_t *node = root;
2849 root = RIGHT(root);
2850 RIGHT(node) = NULL;
2851 } else if (DOWN(root) != NULL) {
2852 dns_rbtnode_t *node = root;
2853 root = DOWN(root);
2854 DOWN(node) = NULL;
2855 } else {
2856 /*
2857 * There are no left, right or down nodes, so we
2858 * can free this one and go back to its parent.
2859 */
2860 dns_rbtnode_t *node = root;
2861 root = PARENT(root);
2862
2863 if (DATA(node) != NULL && rbt->data_deleter != NULL)
2864 rbt->data_deleter(DATA(node),
2865 rbt->deleter_arg);
2866 if (unhash)
2867 unhash_node(rbt, node);
2868 /*
2869 * Note: we don't call unhash_node() here as we
2870 * are destroying the complete RBT tree.
2871 */
2872 #if DNS_RBT_USEMAGIC
2873 node->magic = 0;
2874 #endif
2875 freenode(rbt, &node);
2876 if (quantum != 0 && --quantum == 0)
2877 break;
2878 }
2879 }
2880
2881 *nodep = root;
2882 }
2883
2884 static size_t
2885 getheight_helper(dns_rbtnode_t *node) {
2886 size_t dl, dr;
2887 size_t this_height, down_height;
2888
2889 if (node == NULL)
2890 return (0);
2891
2892 dl = getheight_helper(LEFT(node));
2893 dr = getheight_helper(RIGHT(node));
2894
2895 this_height = ISC_MAX(dl + 1, dr + 1);
2896 down_height = getheight_helper(DOWN(node));
2897
2898 return (ISC_MAX(this_height, down_height));
2899 }
2900
2901 size_t
2902 dns__rbt_getheight(dns_rbt_t *rbt) {
2903 return (getheight_helper(rbt->root));
2904 }
2905
2906 static isc_boolean_t
2907 check_properties_helper(dns_rbtnode_t *node) {
2908 if (node == NULL)
2909 return (ISC_TRUE);
2910
2911 if (IS_RED(node)) {
2912 /* Root nodes must be BLACK. */
2913 if (IS_ROOT(node))
2914 return (ISC_FALSE);
2915
2916 /* Both children of RED nodes must be BLACK. */
2917 if (IS_RED(LEFT(node)) || IS_RED(RIGHT(node)))
2918 return (ISC_FALSE);
2919 }
2920
2921 if ((DOWN(node) != NULL) && (!IS_ROOT(DOWN(node))))
2922 return (ISC_FALSE);
2923
2924 if (IS_ROOT(node)) {
2925 if ((PARENT(node) != NULL) &&
2926 (DOWN(PARENT(node)) != node))
2927 return (ISC_FALSE);
2928
2929 if (get_upper_node(node) != PARENT(node))
2930 return (ISC_FALSE);
2931 }
2932
2933 /* If node is assigned to the down_ pointer of its parent, it is
2934 * a subtree root and must have the flag set.
2935 */
2936 if (((!PARENT(node)) ||
2937 (DOWN(PARENT(node)) == node)) &&
2938 (!IS_ROOT(node)))
2939 {
2940 return (ISC_FALSE);
2941 }
2942
2943 /* Repeat tests with this node's children. */
2944 return (check_properties_helper(LEFT(node)) &&
2945 check_properties_helper(RIGHT(node)) &&
2946 check_properties_helper(DOWN(node)));
2947 }
2948
2949 static isc_boolean_t
2950 check_black_distance_helper(dns_rbtnode_t *node, size_t *distance) {
2951 size_t dl, dr, dd;
2952
2953 if (node == NULL) {
2954 *distance = 1;
2955 return (ISC_TRUE);
2956 }
2957
2958 if (!check_black_distance_helper(LEFT(node), &dl))
2959 return (ISC_FALSE);
2960
2961 if (!check_black_distance_helper(RIGHT(node), &dr))
2962 return (ISC_FALSE);
2963
2964 if (!check_black_distance_helper(DOWN(node), &dd))
2965 return (ISC_FALSE);
2966
2967 /* Left and right side black node counts must match. */
2968 if (dl != dr)
2969 return (ISC_FALSE);
2970
2971 if (IS_BLACK(node))
2972 dl++;
2973
2974 *distance = dl;
2975
2976 return (ISC_TRUE);
2977 }
2978
2979 isc_boolean_t
2980 dns__rbt_checkproperties(dns_rbt_t *rbt) {
2981 size_t dd;
2982
2983 if (!check_properties_helper(rbt->root))
2984 return (ISC_FALSE);
2985
2986 /* Path from a given node to all its leaves must contain the
2987 * same number of BLACK child nodes. This is done separately
2988 * here instead of inside check_properties_helper() as
2989 * it would take (n log n) complexity otherwise.
2990 */
2991 return (check_black_distance_helper(rbt->root, &dd));
2992 }
2993
2994 static void
2995 dns_rbt_indent(FILE *f, int depth) {
2996 int i;
2997
2998 fprintf(f, "%4d ", depth);
2999
3000 for (i = 0; i < depth; i++)
3001 fprintf(f, "- ");
3002 }
3003
3004 void
3005 dns_rbt_printnodeinfo(dns_rbtnode_t *n, FILE *f) {
3006 fprintf(f, "Node info for nodename: ");
3007 printnodename(n, ISC_TRUE, f);
3008 fprintf(f, "\n");
3009
3010 fprintf(f, "n = %p\n", n);
3011
3012 fprintf(f, "Relative pointers: %s%s%s%s%s\n",
3013 (n->parent_is_relative == 1 ? " P" : ""),
3014 (n->right_is_relative == 1 ? " R" : ""),
3015 (n->left_is_relative == 1 ? " L" : ""),
3016 (n->down_is_relative == 1 ? " D" : ""),
3017 (n->data_is_relative == 1 ? " T" : ""));
3018
3019 fprintf(f, "node lock address = %u\n", n->locknum);
3020
3021 fprintf(f, "Parent: %p\n", n->parent);
3022 fprintf(f, "Right: %p\n", n->right);
3023 fprintf(f, "Left: %p\n", n->left);
3024 fprintf(f, "Down: %p\n", n->down);
3025 fprintf(f, "daTa: %p\n", n->data);
3026 }
3027
3028 static void
3029 printnodename(dns_rbtnode_t *node, isc_boolean_t quoted, FILE *f) {
3030 isc_region_t r;
3031 dns_name_t name;
3032 char buffer[DNS_NAME_FORMATSIZE];
3033 dns_offsets_t offsets;
3034
3035 r.length = NAMELEN(node);
3036 r.base = NAME(node);
3037
3038 dns_name_init(&name, offsets);
3039 dns_name_fromregion(&name, &r);
3040
3041 dns_name_format(&name, buffer, sizeof(buffer));
3042
3043 if (quoted)
3044 fprintf(f, "\"%s\"", buffer);
3045 else
3046 fprintf(f, "%s", buffer);
3047 }
3048
3049 static void
3050 print_text_helper(dns_rbtnode_t *root, dns_rbtnode_t *parent,
3051 int depth, const char *direction,
3052 void (*data_printer)(FILE *, void *), FILE *f)
3053 {
3054 dns_rbt_indent(f, depth);
3055
3056 if (root != NULL) {
3057 printnodename(root, ISC_TRUE, f);
3058 fprintf(f, " (%s, %s", direction,
3059 IS_RED(root) ? "RED" : "BLACK");
3060
3061 if ((! IS_ROOT(root) && PARENT(root) != parent) ||
3062 ( IS_ROOT(root) && depth > 0 &&
3063 DOWN(PARENT(root)) != root)) {
3064
3065 fprintf(f, " (BAD parent pointer! -> ");
3066 if (PARENT(root) != NULL)
3067 printnodename(PARENT(root), ISC_TRUE, f);
3068 else
3069 fprintf(f, "NULL");
3070 fprintf(f, ")");
3071 }
3072
3073 fprintf(f, ")");
3074
3075 if (root->data != NULL && data_printer != NULL) {
3076 fprintf(f, " data@%p: ", root->data);
3077 data_printer(f, root->data);
3078 }
3079 fprintf(f, "\n");
3080
3081 depth++;
3082
3083 if (IS_RED(root) && IS_RED(LEFT(root)))
3084 fprintf(f, "** Red/Red color violation on left\n");
3085 print_text_helper(LEFT(root), root, depth, "left",
3086 data_printer, f);
3087
3088 if (IS_RED(root) && IS_RED(RIGHT(root)))
3089 fprintf(f, "** Red/Red color violation on right\n");
3090 print_text_helper(RIGHT(root), root, depth, "right",
3091 data_printer, f);
3092
3093 print_text_helper(DOWN(root), NULL, depth, "down",
3094 data_printer, f);
3095 } else {
3096 fprintf(f, "NULL (%s)\n", direction);
3097 }
3098 }
3099
3100 void
3101 dns_rbt_printtext(dns_rbt_t *rbt,
3102 void (*data_printer)(FILE *, void *), FILE *f)
3103 {
3104 REQUIRE(VALID_RBT(rbt));
3105
3106 print_text_helper(rbt->root, NULL, 0, "root", data_printer, f);
3107 }
3108
3109 static int
3110 print_dot_helper(dns_rbtnode_t *node, unsigned int *nodecount,
3111 isc_boolean_t show_pointers, FILE *f)
3112 {
3113 unsigned int l, r, d;
3114
3115 if (node == NULL)
3116 return (0);
3117
3118 l = print_dot_helper(LEFT(node), nodecount, show_pointers, f);
3119 r = print_dot_helper(RIGHT(node), nodecount, show_pointers, f);
3120 d = print_dot_helper(DOWN(node), nodecount, show_pointers, f);
3121
3122 *nodecount += 1;
3123
3124 fprintf(f, "node%u[label = \"<f0> |<f1> ", *nodecount);
3125 printnodename(node, ISC_FALSE, f);
3126 fprintf(f, "|<f2>");
3127
3128 if (show_pointers)
3129 fprintf(f, "|<f3> n=%p|<f4> p=%p", node, PARENT(node));
3130
3131 fprintf(f, "\"] [");
3132
3133 if (IS_RED(node))
3134 fprintf(f, "color=red");
3135 else
3136 fprintf(f, "color=black");
3137
3138 /* XXXMUKS: verify that IS_ROOT() indicates subtree root and not
3139 * forest root.
3140 */
3141 if (IS_ROOT(node))
3142 fprintf(f, ",penwidth=3");
3143
3144 if (IS_EMPTY(node))
3145 fprintf(f, ",style=filled,fillcolor=lightgrey");
3146
3147 fprintf(f, "];\n");
3148
3149 if (LEFT(node) != NULL)
3150 fprintf(f, "\"node%u\":f0 -> \"node%u\":f1;\n", *nodecount, l);
3151
3152 if (DOWN(node) != NULL)
3153 fprintf(f, "\"node%u\":f1 -> \"node%u\":f1 [penwidth=5];\n",
3154 *nodecount, d);
3155
3156 if (RIGHT(node) != NULL)
3157 fprintf(f, "\"node%u\":f2 -> \"node%u\":f1;\n", *nodecount, r);
3158
3159 return (*nodecount);
3160 }
3161
3162 void
3163 dns_rbt_printdot(dns_rbt_t *rbt, isc_boolean_t show_pointers, FILE *f) {
3164 unsigned int nodecount = 0;
3165
3166 REQUIRE(VALID_RBT(rbt));
3167
3168 fprintf(f, "digraph g {\n");
3169 fprintf(f, "node [shape = record,height=.1];\n");
3170 print_dot_helper(rbt->root, &nodecount, show_pointers, f);
3171 fprintf(f, "}\n");
3172 }
3173
3174 /*
3175 * Chain Functions
3176 */
3177
3178 void
3179 dns_rbtnodechain_init(dns_rbtnodechain_t *chain, isc_mem_t *mctx) {
3180
3181 REQUIRE(chain != NULL);
3182
3183 /*
3184 * Initialize 'chain'.
3185 */
3186 chain->mctx = mctx;
3187 chain->end = NULL;
3188 chain->level_count = 0;
3189 chain->level_matches = 0;
3190 memset(chain->levels, 0, sizeof(chain->levels));
3191
3192 chain->magic = CHAIN_MAGIC;
3193 }
3194
3195 isc_result_t
3196 dns_rbtnodechain_current(dns_rbtnodechain_t *chain, dns_name_t *name,
3197 dns_name_t *origin, dns_rbtnode_t **node)
3198 {
3199 isc_result_t result = ISC_R_SUCCESS;
3200
3201 REQUIRE(VALID_CHAIN(chain));
3202
3203 if (node != NULL)
3204 *node = chain->end;
3205
3206 if (chain->end == NULL)
3207 return (ISC_R_NOTFOUND);
3208
3209 if (name != NULL) {
3210 NODENAME(chain->end, name);
3211
3212 if (chain->level_count == 0) {
3213 /*
3214 * Names in the top level tree are all absolute.
3215 * Always make 'name' relative.
3216 */
3217 INSIST(dns_name_isabsolute(name));
3218
3219 /*
3220 * This is cheaper than dns_name_getlabelsequence().
3221 */
3222 name->labels--;
3223 name->length--;
3224 name->attributes &= ~DNS_NAMEATTR_ABSOLUTE;
3225 }
3226 }
3227
3228 if (origin != NULL) {
3229 if (chain->level_count > 0)
3230 result = chain_name(chain, origin, ISC_FALSE);
3231 else
3232 result = dns_name_copy(dns_rootname, origin, NULL);
3233 }
3234
3235 return (result);
3236 }
3237
3238 isc_result_t
3239 dns_rbtnodechain_prev(dns_rbtnodechain_t *chain, dns_name_t *name,
3240 dns_name_t *origin)
3241 {
3242 dns_rbtnode_t *current, *previous, *predecessor;
3243 isc_result_t result = ISC_R_SUCCESS;
3244 isc_boolean_t new_origin = ISC_FALSE;
3245
3246 REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
3247
3248 predecessor = NULL;
3249
3250 current = chain->end;
3251
3252 if (LEFT(current) != NULL) {
3253 /*
3254 * Moving left one then right as far as possible is the
3255 * previous node, at least for this level.
3256 */
3257 current = LEFT(current);
3258
3259 while (RIGHT(current) != NULL)
3260 current = RIGHT(current);
3261
3262 predecessor = current;
3263
3264 } else {
3265 /*
3266 * No left links, so move toward the root. If at any point on
3267 * the way there the link from parent to child is a right
3268 * link, then the parent is the previous node, at least
3269 * for this level.
3270 */
3271 while (! IS_ROOT(current)) {
3272 previous = current;
3273 current = PARENT(current);
3274
3275 if (RIGHT(current) == previous) {
3276 predecessor = current;
3277 break;
3278 }
3279 }
3280 }
3281
3282 if (predecessor != NULL) {
3283 /*
3284 * Found a predecessor node in this level. It might not
3285 * really be the predecessor, however.
3286 */
3287 if (DOWN(predecessor) != NULL) {
3288 /*
3289 * The predecessor is really down at least one level.
3290 * Go down and as far right as possible, and repeat
3291 * as long as the rightmost node has a down pointer.
3292 */
3293 do {
3294 /*
3295 * XXX DCL Need to do something about origins
3296 * here. See whether to go down, and if so
3297 * whether it is truly what Bob calls a
3298 * new origin.
3299 */
3300 ADD_LEVEL(chain, predecessor);
3301 predecessor = DOWN(predecessor);
3302
3303 /* XXX DCL duplicated from above; clever
3304 * way to unduplicate? */
3305
3306 while (RIGHT(predecessor) != NULL)
3307 predecessor = RIGHT(predecessor);
3308 } while (DOWN(predecessor) != NULL);
3309
3310 /* XXX DCL probably needs work on the concept */
3311 if (origin != NULL)
3312 new_origin = ISC_TRUE;
3313 }
3314
3315 } else if (chain->level_count > 0) {
3316 /*
3317 * Dang, didn't find a predecessor in this level.
3318 * Got to the root of this level without having traversed
3319 * any right links. Ascend the tree one level; the
3320 * node that points to this tree is the predecessor.
3321 */
3322 INSIST(chain->level_count > 0 && IS_ROOT(current));
3323 predecessor = chain->levels[--chain->level_count];
3324
3325 /* XXX DCL probably needs work on the concept */
3326 /*
3327 * Don't declare an origin change when the new origin is "."
3328 * at the top level tree, because "." is declared as the origin
3329 * for the second level tree.
3330 */
3331 if (origin != NULL &&
3332 (chain->level_count > 0 || OFFSETLEN(predecessor) > 1))
3333 new_origin = ISC_TRUE;
3334 }
3335
3336 if (predecessor != NULL) {
3337 chain->end = predecessor;
3338
3339 if (new_origin) {
3340 result = dns_rbtnodechain_current(chain, name, origin,
3341 NULL);
3342 if (result == ISC_R_SUCCESS)
3343 result = DNS_R_NEWORIGIN;
3344
3345 } else
3346 result = dns_rbtnodechain_current(chain, name, NULL,
3347 NULL);
3348
3349 } else
3350 result = ISC_R_NOMORE;
3351
3352 return (result);
3353 }
3354
3355 isc_result_t
3356 dns_rbtnodechain_down(dns_rbtnodechain_t *chain, dns_name_t *name,
3357 dns_name_t *origin)
3358 {
3359 dns_rbtnode_t *current, *successor;
3360 isc_result_t result = ISC_R_SUCCESS;
3361 isc_boolean_t new_origin = ISC_FALSE;
3362
3363 REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
3364
3365 successor = NULL;
3366
3367 current = chain->end;
3368
3369 if (DOWN(current) != NULL) {
3370 /*
3371 * Don't declare an origin change when the new origin is "."
3372 * at the second level tree, because "." is already declared
3373 * as the origin for the top level tree.
3374 */
3375 if (chain->level_count > 0 ||
3376 OFFSETLEN(current) > 1)
3377 new_origin = ISC_TRUE;
3378
3379 ADD_LEVEL(chain, current);
3380 current = DOWN(current);
3381
3382 while (LEFT(current) != NULL)
3383 current = LEFT(current);
3384
3385 successor = current;
3386 }
3387
3388 if (successor != NULL) {
3389 chain->end = successor;
3390
3391 /*
3392 * It is not necessary to use dns_rbtnodechain_current like
3393 * the other functions because this function will never
3394 * find a node in the topmost level. This is because the
3395 * root level will never be more than one name, and everything
3396 * in the megatree is a successor to that node, down at
3397 * the second level or below.
3398 */
3399
3400 if (name != NULL)
3401 NODENAME(chain->end, name);
3402
3403 if (new_origin) {
3404 if (origin != NULL)
3405 result = chain_name(chain, origin, ISC_FALSE);
3406
3407 if (result == ISC_R_SUCCESS)
3408 result = DNS_R_NEWORIGIN;
3409
3410 } else
3411 result = ISC_R_SUCCESS;
3412
3413 } else
3414 result = ISC_R_NOMORE;
3415
3416 return (result);
3417 }
3418
3419 isc_result_t
3420 dns_rbtnodechain_nextflat(dns_rbtnodechain_t *chain, dns_name_t *name) {
3421 dns_rbtnode_t *current, *previous, *successor;
3422 isc_result_t result = ISC_R_SUCCESS;
3423
3424 REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
3425
3426 successor = NULL;
3427
3428 current = chain->end;
3429
3430 if (RIGHT(current) == NULL) {
3431 while (! IS_ROOT(current)) {
3432 previous = current;
3433 current = PARENT(current);
3434
3435 if (LEFT(current) == previous) {
3436 successor = current;
3437 break;
3438 }
3439 }
3440 } else {
3441 current = RIGHT(current);
3442
3443 while (LEFT(current) != NULL)
3444 current = LEFT(current);
3445
3446 successor = current;
3447 }
3448
3449 if (successor != NULL) {
3450 chain->end = successor;
3451
3452 if (name != NULL)
3453 NODENAME(chain->end, name);
3454
3455 result = ISC_R_SUCCESS;
3456 } else
3457 result = ISC_R_NOMORE;
3458
3459 return (result);
3460 }
3461
3462 isc_result_t
3463 dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name,
3464 dns_name_t *origin)
3465 {
3466 dns_rbtnode_t *current, *previous, *successor;
3467 isc_result_t result = ISC_R_SUCCESS;
3468 isc_boolean_t new_origin = ISC_FALSE;
3469
3470 REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
3471
3472 successor = NULL;
3473
3474 current = chain->end;
3475
3476 /*
3477 * If there is a level below this node, the next node is the leftmost
3478 * node of the next level.
3479 */
3480 if (DOWN(current) != NULL) {
3481 /*
3482 * Don't declare an origin change when the new origin is "."
3483 * at the second level tree, because "." is already declared
3484 * as the origin for the top level tree.
3485 */
3486 if (chain->level_count > 0 ||
3487 OFFSETLEN(current) > 1)
3488 new_origin = ISC_TRUE;
3489
3490 ADD_LEVEL(chain, current);
3491 current = DOWN(current);
3492
3493 while (LEFT(current) != NULL)
3494 current = LEFT(current);
3495
3496 successor = current;
3497
3498 } else if (RIGHT(current) == NULL) {
3499 /*
3500 * The successor is up, either in this level or a previous one.
3501 * Head back toward the root of the tree, looking for any path
3502 * that was via a left link; the successor is the node that has
3503 * that left link. In the event the root of the level is
3504 * reached without having traversed any left links, ascend one
3505 * level and look for either a right link off the point of
3506 * ascent, or search for a left link upward again, repeating
3507 * ascends until either case is true.
3508 */
3509 do {
3510 while (! IS_ROOT(current)) {
3511 previous = current;
3512 current = PARENT(current);
3513
3514 if (LEFT(current) == previous) {
3515 successor = current;
3516 break;
3517 }
3518 }
3519
3520 if (successor == NULL) {
3521 /*
3522 * Reached the root without having traversed
3523 * any left pointers, so this level is done.
3524 */
3525 if (chain->level_count == 0) {
3526 /*
3527 * If the tree we are iterating over
3528 * was modified since this chain was
3529 * initialized in a way that caused
3530 * node splits to occur, "current" may
3531 * now be pointing to a root node which
3532 * appears to be at level 0, but still
3533 * has a parent. If that happens,
3534 * abort. Otherwise, we are done
3535 * looking for a successor as we really
3536 * reached the root node on level 0.
3537 */
3538 INSIST(PARENT(current) == NULL);
3539 break;
3540 }
3541
3542 current = chain->levels[--chain->level_count];
3543 new_origin = ISC_TRUE;
3544
3545 if (RIGHT(current) != NULL)
3546 break;
3547 }
3548 } while (successor == NULL);
3549 }
3550
3551 if (successor == NULL && RIGHT(current) != NULL) {
3552 current = RIGHT(current);
3553
3554 while (LEFT(current) != NULL)
3555 current = LEFT(current);
3556
3557 successor = current;
3558 }
3559
3560 if (successor != NULL) {
3561 /*
3562 * If we determine that the current node is the successor to
3563 * itself, we will run into an infinite loop, so abort instead.
3564 */
3565 INSIST(chain->end != successor);
3566
3567 chain->end = successor;
3568
3569 /*
3570 * It is not necessary to use dns_rbtnodechain_current like
3571 * the other functions because this function will never
3572 * find a node in the topmost level. This is because the
3573 * root level will never be more than one name, and everything
3574 * in the megatree is a successor to that node, down at
3575 * the second level or below.
3576 */
3577
3578 if (name != NULL)
3579 NODENAME(chain->end, name);
3580
3581 if (new_origin) {
3582 if (origin != NULL)
3583 result = chain_name(chain, origin, ISC_FALSE);
3584
3585 if (result == ISC_R_SUCCESS)
3586 result = DNS_R_NEWORIGIN;
3587
3588 } else
3589 result = ISC_R_SUCCESS;
3590
3591 } else
3592 result = ISC_R_NOMORE;
3593
3594 return (result);
3595 }
3596
3597 isc_result_t
3598 dns_rbtnodechain_first(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
3599 dns_name_t *name, dns_name_t *origin)
3600
3601 {
3602 isc_result_t result;
3603
3604 REQUIRE(VALID_RBT(rbt));
3605 REQUIRE(VALID_CHAIN(chain));
3606
3607 dns_rbtnodechain_reset(chain);
3608
3609 chain->end = rbt->root;
3610
3611 result = dns_rbtnodechain_current(chain, name, origin, NULL);
3612
3613 if (result == ISC_R_SUCCESS)
3614 result = DNS_R_NEWORIGIN;
3615
3616 return (result);
3617 }
3618
3619 isc_result_t
3620 dns_rbtnodechain_last(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
3621 dns_name_t *name, dns_name_t *origin)
3622
3623 {
3624 isc_result_t result;
3625
3626 REQUIRE(VALID_RBT(rbt));
3627 REQUIRE(VALID_CHAIN(chain));
3628
3629 dns_rbtnodechain_reset(chain);
3630
3631 result = move_chain_to_last(chain, rbt->root);
3632 if (result != ISC_R_SUCCESS)
3633 return (result);
3634
3635 result = dns_rbtnodechain_current(chain, name, origin, NULL);
3636
3637 if (result == ISC_R_SUCCESS)
3638 result = DNS_R_NEWORIGIN;
3639
3640 return (result);
3641 }
3642
3643
3644 void
3645 dns_rbtnodechain_reset(dns_rbtnodechain_t *chain) {
3646
3647 REQUIRE(VALID_CHAIN(chain));
3648
3649 /*
3650 * Free any dynamic storage associated with 'chain', and then
3651 * reinitialize 'chain'.
3652 */
3653 chain->end = NULL;
3654 chain->level_count = 0;
3655 chain->level_matches = 0;
3656 }
3657
3658 void
3659 dns_rbtnodechain_invalidate(dns_rbtnodechain_t *chain) {
3660 /*
3661 * Free any dynamic storage associated with 'chain', and then
3662 * invalidate 'chain'.
3663 */
3664
3665 dns_rbtnodechain_reset(chain);
3666
3667 chain->magic = 0;
3668 }
3669
3670 /* XXXMUKS:
3671 *
3672 * - worth removing inline as static functions are inlined automatically
3673 * where suitable by modern compilers.
3674 * - bump the size of dns_rbt.nodecount to size_t.
3675 * - the dumpfile header also contains a nodecount that is unsigned
3676 * int. If large files (> 2^32 nodes) are to be supported, the
3677 * allocation for this field should be increased.
3678 */
3679