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