1999-09-23 21:58:03 +00:00
|
|
|
/*
|
2013-01-25 23:45:56 +00:00
|
|
|
* Copyright (C) 2004, 2005, 2007-2009, 2011-2013 Internet Systems Consortium, Inc. ("ISC")
|
2004-03-05 05:14:21 +00:00
|
|
|
* Copyright (C) 1999-2003 Internet Software Consortium.
|
2000-08-01 01:33:37 +00:00
|
|
|
*
|
2007-06-18 23:47:57 +00:00
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
1999-01-20 10:13:43 +00:00
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
2000-08-01 01:33:37 +00:00
|
|
|
*
|
2004-03-05 05:14:21 +00:00
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
|
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
|
|
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
|
|
|
|
2012-03-08 00:21:15 +11:00
|
|
|
/* $Id$ */
|
2005-04-27 04:57:32 +00:00
|
|
|
|
|
|
|
/*! \file */
|
1999-03-18 21:21:31 +00:00
|
|
|
|
|
|
|
/* Principal Authors: DCL */
|
|
|
|
|
1999-01-22 04:35:11 +00:00
|
|
|
#include <config.h>
|
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
#include <sys/stat.h>
|
2013-03-12 20:44:41 -07:00
|
|
|
#ifdef HAVE_INTTYPES_H
|
|
|
|
#include <inttypes.h> /* uintptr_t */
|
|
|
|
#endif
|
2012-06-20 14:13:12 -05:00
|
|
|
|
|
|
|
#include <isc/file.h>
|
1999-01-20 10:13:43 +00:00
|
|
|
#include <isc/mem.h>
|
2004-03-04 06:56:41 +00:00
|
|
|
#include <isc/platform.h>
|
2001-03-09 23:38:00 +00:00
|
|
|
#include <isc/print.h>
|
2005-06-04 05:32:50 +00:00
|
|
|
#include <isc/refcount.h>
|
2012-06-20 14:13:12 -05:00
|
|
|
#include <isc/socket.h>
|
|
|
|
#include <isc/stdio.h>
|
2000-05-19 04:42:08 +00:00
|
|
|
#include <isc/string.h>
|
2000-04-28 01:08:52 +00:00
|
|
|
#include <isc/util.h>
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2000-07-14 19:17:39 +00:00
|
|
|
* This define is so dns/name.h (included by dns/fixedname.h) uses more
|
|
|
|
* efficient macro calls instead of functions for a few operations.
|
|
|
|
*/
|
|
|
|
#define DNS_NAME_USEINLINE 1
|
|
|
|
|
|
|
|
#include <dns/fixedname.h>
|
2007-10-19 17:15:53 +00:00
|
|
|
#include <dns/log.h>
|
1999-01-25 15:46:30 +00:00
|
|
|
#include <dns/rbt.h>
|
|
|
|
#include <dns/result.h>
|
2012-06-20 14:13:12 -05:00
|
|
|
#include <dns/version.h>
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#define CHECK(x) \
|
|
|
|
do { \
|
|
|
|
result = (x); \
|
|
|
|
if (result != ISC_R_SUCCESS) \
|
|
|
|
goto cleanup; \
|
|
|
|
} while (0)
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2007-10-19 17:15:53 +00:00
|
|
|
#define RBT_MAGIC ISC_MAGIC('R', 'B', 'T', '+')
|
|
|
|
#define VALID_RBT(rbt) ISC_MAGIC_VALID(rbt, RBT_MAGIC)
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2000-04-12 21:38:04 +00:00
|
|
|
/*
|
2000-05-08 14:38:29 +00:00
|
|
|
* XXXDCL Since parent pointers were added in again, I could remove all of the
|
|
|
|
* chain junk, and replace with dns_rbt_firstnode, _previousnode, _nextnode,
|
|
|
|
* _lastnode. This would involve pretty major change to the API.
|
2000-04-12 21:38:04 +00:00
|
|
|
*/
|
2007-10-19 17:15:53 +00:00
|
|
|
#define CHAIN_MAGIC ISC_MAGIC('0', '-', '0', '-')
|
|
|
|
#define VALID_CHAIN(chain) ISC_MAGIC_VALID(chain, CHAIN_MAGIC)
|
1999-04-09 15:21:15 +00:00
|
|
|
|
2007-10-19 17:15:53 +00:00
|
|
|
#define RBT_HASH_SIZE 64
|
2000-10-25 07:21:31 +00:00
|
|
|
|
|
|
|
#ifdef RBT_MEM_TEST
|
|
|
|
#undef RBT_HASH_SIZE
|
2007-10-19 17:15:53 +00:00
|
|
|
#define RBT_HASH_SIZE 2 /*%< To give the reallocation code a workout. */
|
2000-10-25 07:21:31 +00:00
|
|
|
#endif
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
struct dns_rbt {
|
2008-01-22 23:28:04 +00:00
|
|
|
unsigned int magic;
|
|
|
|
isc_mem_t * mctx;
|
|
|
|
dns_rbtnode_t * root;
|
|
|
|
void (*data_deleter)(void *, void *);
|
|
|
|
void * deleter_arg;
|
|
|
|
unsigned int nodecount;
|
|
|
|
unsigned int hashsize;
|
|
|
|
dns_rbtnode_t ** hashtable;
|
2012-06-20 14:13:12 -05:00
|
|
|
void * mmap_location;
|
1999-01-31 00:52:53 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#define RED 0
|
|
|
|
#define BLACK 1
|
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
/*
|
2013-01-24 14:20:48 -08:00
|
|
|
* This is the header for map-format RBT images. It is populated,
|
2012-06-20 14:13:12 -05:00
|
|
|
* and then written, as the LAST thing done to the file before returning.
|
|
|
|
* Writing this last (with zeros in the header area initially) will ensure
|
|
|
|
* that the header is only valid when the RBT image is also valid.
|
|
|
|
*/
|
|
|
|
typedef struct file_header file_header_t;
|
|
|
|
|
|
|
|
/* Pad to 32 bytes */
|
|
|
|
static char FILE_VERSION[32] = "\0";
|
|
|
|
|
|
|
|
/* Header length, always the same size regardless of structure size */
|
|
|
|
const unsigned int HEADER_LENGTH = 1024;
|
|
|
|
|
|
|
|
struct file_header {
|
|
|
|
char version1[32];
|
|
|
|
isc_uint64_t first_node_offset; /* usually 1024 */
|
2012-06-20 23:46:40 +00:00
|
|
|
/*
|
2013-01-24 14:20:48 -08:00
|
|
|
* information about the system on which the map file was generated
|
|
|
|
* will be used to tell if we can load the map file or not
|
2012-06-20 14:13:12 -05:00
|
|
|
*/
|
|
|
|
isc_uint32_t ptrsize;
|
|
|
|
unsigned int bigendian:1; /* big or little endian system */
|
|
|
|
unsigned int rdataset_fixed:1; /* compiled with --enable-rrset-fixed */
|
|
|
|
unsigned int nodecount; /* shadow from rbt structure */
|
|
|
|
char version2[32]; /* repeated; must match version1 */
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The following declarations are for the serialization of an RBT:
|
|
|
|
*
|
|
|
|
* step one: write out a zeroed header of 1024 bytes
|
|
|
|
* step two: walk the tree in a depth-first, left-right-down order, writing
|
|
|
|
* out the nodes, reserving space as we go, correcting addresses to point
|
2012-06-20 23:46:40 +00:00
|
|
|
* at the proper offset in the file, and setting a flag for each pointer to
|
|
|
|
* indicate that it is a reference to a location in the file, rather than in
|
2012-06-20 14:13:12 -05:00
|
|
|
* memory.
|
2012-06-20 23:46:40 +00:00
|
|
|
* step three: write out the header, adding the information that will be
|
2012-06-20 14:13:12 -05:00
|
|
|
* needed to re-create the tree object itself.
|
|
|
|
*
|
|
|
|
* The RBTDB object will do this three times, once for each of the three
|
|
|
|
* RBT objects it contains.
|
|
|
|
*
|
|
|
|
* Note: 'file' must point an actual open file that can be mmapped
|
|
|
|
* and fseeked, not to a pipe or stream
|
|
|
|
*/
|
|
|
|
|
|
|
|
static isc_result_t
|
|
|
|
dns_rbt_zero_header(FILE *file);
|
|
|
|
|
|
|
|
static isc_result_t
|
|
|
|
write_header(FILE *file, dns_rbt_t *rbt, isc_uint64_t first_node_offset);
|
|
|
|
|
|
|
|
static isc_result_t
|
2012-06-21 06:59:59 +00:00
|
|
|
serialize_node(FILE *file, dns_rbtnode_t *node, uintptr_t left,
|
|
|
|
uintptr_t right, uintptr_t down, uintptr_t parent,
|
|
|
|
uintptr_t data);
|
2012-06-20 14:13:12 -05:00
|
|
|
|
|
|
|
static isc_result_t
|
2012-06-21 06:59:59 +00:00
|
|
|
serialize_nodes(FILE *file, dns_rbtnode_t *node, uintptr_t parent,
|
2012-06-20 14:13:12 -05:00
|
|
|
dns_rbtdatawriter_t datawriter, isc_uint32_t serial,
|
2012-06-21 06:59:59 +00:00
|
|
|
uintptr_t *where);
|
2012-06-20 14:13:12 -05:00
|
|
|
/*
|
2012-06-20 23:46:40 +00:00
|
|
|
* The following functions allow you to get the actual address of a pointer
|
|
|
|
* without having to use an if statement to check to see if that address is
|
2012-06-20 14:13:12 -05:00
|
|
|
* relative or not
|
|
|
|
*/
|
|
|
|
static inline dns_rbtnode_t *
|
|
|
|
getparent(dns_rbtnode_t *node, file_header_t *header) {
|
|
|
|
char *adjusted_address = (char *)(node->parent);
|
2012-06-21 06:59:59 +00:00
|
|
|
adjusted_address += node->parent_is_relative * (uintptr_t)header;
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
return ((dns_rbtnode_t *)adjusted_address);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline dns_rbtnode_t *
|
|
|
|
getleft(dns_rbtnode_t *node, file_header_t *header) {
|
|
|
|
char *adjusted_address = (char *)(node->left);
|
2012-06-21 06:59:59 +00:00
|
|
|
adjusted_address += node->left_is_relative * (uintptr_t)header;
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
return ((dns_rbtnode_t *)adjusted_address);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline dns_rbtnode_t *
|
|
|
|
getright(dns_rbtnode_t *node, file_header_t *header) {
|
|
|
|
char *adjusted_address = (char *)(node->right);
|
2012-06-21 06:59:59 +00:00
|
|
|
adjusted_address += node->right_is_relative * (uintptr_t)header;
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
return ((dns_rbtnode_t *)adjusted_address);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline dns_rbtnode_t *
|
|
|
|
getdown(dns_rbtnode_t *node, file_header_t *header) {
|
|
|
|
char *adjusted_address = (char *)(node->down);
|
2012-06-21 06:59:59 +00:00
|
|
|
adjusted_address += node->down_is_relative * (uintptr_t)header;
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
return ((dns_rbtnode_t *)adjusted_address);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline dns_rbtnode_t *
|
|
|
|
getdata(dns_rbtnode_t *node, file_header_t *header) {
|
|
|
|
char *adjusted_address = (char *)(node->data);
|
2012-06-21 06:59:59 +00:00
|
|
|
adjusted_address += node->data_is_relative * (uintptr_t)header;
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
return ((dns_rbtnode_t *)adjusted_address);
|
|
|
|
}
|
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-03-11 18:54:31 +00:00
|
|
|
* Elements of the rbtnode structure.
|
|
|
|
*/
|
2007-10-19 17:15:53 +00:00
|
|
|
#define PARENT(node) ((node)->parent)
|
|
|
|
#define LEFT(node) ((node)->left)
|
|
|
|
#define RIGHT(node) ((node)->right)
|
|
|
|
#define DOWN(node) ((node)->down)
|
|
|
|
#define DATA(node) ((node)->data)
|
|
|
|
#define HASHNEXT(node) ((node)->hashnext)
|
|
|
|
#define HASHVAL(node) ((node)->hashval)
|
|
|
|
#define COLOR(node) ((node)->color)
|
|
|
|
#define NAMELEN(node) ((node)->namelen)
|
2009-10-20 04:57:57 +00:00
|
|
|
#define OLDNAMELEN(node) ((node)->oldnamelen)
|
2007-10-19 17:15:53 +00:00
|
|
|
#define OFFSETLEN(node) ((node)->offsetlen)
|
|
|
|
#define ATTRS(node) ((node)->attributes)
|
|
|
|
#define IS_ROOT(node) ISC_TF((node)->is_root == 1)
|
|
|
|
#define FINDCALLBACK(node) ISC_TF((node)->find_callback == 1)
|
1999-03-11 18:54:31 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-03-11 18:54:31 +00:00
|
|
|
* Structure elements from the rbtdb.c, not
|
|
|
|
* used as part of the rbt.c algorithms.
|
|
|
|
*/
|
2007-10-19 17:15:53 +00:00
|
|
|
#define DIRTY(node) ((node)->dirty)
|
|
|
|
#define WILD(node) ((node)->wild)
|
|
|
|
#define LOCKNUM(node) ((node)->locknum)
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
2009-10-20 04:57:57 +00:00
|
|
|
* The variable length stuff stored after the node has the following
|
|
|
|
* structure.
|
|
|
|
*
|
|
|
|
* <name_data>{1..255}<oldoffsetlen>{1}<offsets>{1..128}
|
|
|
|
*
|
|
|
|
* <name_data> contains the name of the node when it was created.
|
|
|
|
* <oldoffsetlen> contains the length of <offsets> when the node was created.
|
|
|
|
* <offsets> contains the offets into name for each label when the node was
|
|
|
|
* created.
|
1999-03-11 18:54:31 +00:00
|
|
|
*/
|
2009-10-20 04:57:57 +00:00
|
|
|
|
2007-10-19 17:15:53 +00:00
|
|
|
#define NAME(node) ((unsigned char *)((node) + 1))
|
2009-10-20 04:57:57 +00:00
|
|
|
#define OFFSETS(node) (NAME(node) + OLDNAMELEN(node) + 1)
|
|
|
|
#define OLDOFFSETLEN(node) (OFFSETS(node)[-1])
|
1999-03-11 18:54:31 +00:00
|
|
|
|
2007-10-19 17:15:53 +00:00
|
|
|
#define NODE_SIZE(node) (sizeof(*node) + \
|
2009-10-20 04:57:57 +00:00
|
|
|
OLDNAMELEN(node) + OLDOFFSETLEN(node) + 1)
|
1999-03-11 18:54:31 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-03-11 18:54:31 +00:00
|
|
|
* Color management.
|
|
|
|
*/
|
2007-10-19 17:15:53 +00:00
|
|
|
#define IS_RED(node) ((node) != NULL && (node)->color == RED)
|
|
|
|
#define IS_BLACK(node) ((node) == NULL || (node)->color == BLACK)
|
|
|
|
#define MAKE_RED(node) ((node)->color = RED)
|
|
|
|
#define MAKE_BLACK(node) ((node)->color = BLACK)
|
1999-01-31 00:52:53 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-04-09 15:21:15 +00:00
|
|
|
* Chain management.
|
2000-10-25 07:21:31 +00:00
|
|
|
*
|
|
|
|
* The "ancestors" member of chains were removed, with their job now
|
2009-01-17 14:45:17 +00:00
|
|
|
* being wholly handled by parent pointers (which didn't exist, because
|
2000-10-25 07:21:31 +00:00
|
|
|
* of memory concerns, when chains were first implemented).
|
1999-04-09 15:21:15 +00:00
|
|
|
*/
|
1999-03-11 18:54:31 +00:00
|
|
|
#define ADD_LEVEL(chain, node) \
|
2008-01-22 23:28:04 +00:00
|
|
|
(chain)->levels[(chain)->level_count++] = (node)
|
1999-02-06 01:27:35 +00:00
|
|
|
|
2005-04-27 04:57:32 +00:00
|
|
|
/*%
|
1999-02-06 01:27:35 +00:00
|
|
|
* The following macros directly access normally private name variables.
|
|
|
|
* These macros are used to avoid a lot of function calls in the critical
|
|
|
|
* path of the tree traversal code.
|
|
|
|
*/
|
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
static inline void
|
|
|
|
NODENAME(dns_rbtnode_t *node, dns_name_t *name)
|
|
|
|
{
|
|
|
|
name->length = NAMELEN(node);
|
|
|
|
name->labels = OFFSETLEN(node);
|
|
|
|
name->ndata = NAME(node);
|
|
|
|
name->offsets = OFFSETS(node);
|
|
|
|
name->attributes = ATTRS(node);
|
|
|
|
name->attributes |= DNS_NAMEATTR_READONLY;
|
|
|
|
}
|
1999-02-06 01:27:35 +00:00
|
|
|
|
2004-03-03 22:40:56 +00:00
|
|
|
#ifdef DNS_RBT_USEHASH
|
|
|
|
static isc_result_t
|
|
|
|
inithash(dns_rbt_t *rbt);
|
|
|
|
#endif
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
#define inline
|
|
|
|
/*
|
|
|
|
* A little something to help out in GDB.
|
|
|
|
*/
|
1999-03-12 05:00:32 +00:00
|
|
|
dns_name_t Name(dns_rbtnode_t *node);
|
2000-08-01 01:33:37 +00:00
|
|
|
dns_name_t
|
1999-01-25 15:46:30 +00:00
|
|
|
Name(dns_rbtnode_t *node) {
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_name_t name;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_name_init(&name, NULL);
|
|
|
|
if (node != NULL)
|
|
|
|
NODENAME(node, &name);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
return (name);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
1999-10-16 19:44:54 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
static void printnodename(dns_rbtnode_t *node);
|
1999-01-20 10:13:43 +00:00
|
|
|
#endif
|
|
|
|
|
2000-10-25 07:21:31 +00:00
|
|
|
static inline dns_rbtnode_t *
|
|
|
|
find_up(dns_rbtnode_t *node) {
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnode_t *root;
|
2000-10-25 07:21:31 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
/*
|
|
|
|
* Return the node in the level above the argument node that points
|
|
|
|
* to the level the argument node is in. If the argument node is in
|
|
|
|
* the top level, the return value is NULL.
|
|
|
|
*/
|
|
|
|
for (root = node; ! IS_ROOT(root); root = PARENT(root))
|
|
|
|
; /* Nothing. */
|
2000-10-25 07:21:31 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
return (PARENT(root));
|
2000-10-25 07:21:31 +00:00
|
|
|
}
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
|
|
|
* Forward declarations.
|
|
|
|
*/
|
2000-05-19 04:42:08 +00:00
|
|
|
static isc_result_t
|
|
|
|
create_node(isc_mem_t *mctx, dns_name_t *name, dns_rbtnode_t **nodep);
|
join_nodes is removed; node joining is no longer done at any time, because
it corrupts active chains and can change the data at a node which
someone might be using. (In the latter case, though the full name from
the root was still intact, and the ->data member was not altered, other
values like the partial name ndata and offsets could change.)
New functions dns_rbt_fullnamefromnode() and dns_rbt_formatnodename(),
for getting a dns_name_t and filling a char buffer, respectively, with
the complete name of a node from the root.
rbtnode.is_root was being treated as isc_boolean_t even though it was bitfield
of width 1. This worked fine, but wasn't consistent with ISC style, and
would have puked if someone decided to change isc_true from 1 (unlikely
as that is). Yeah, anal, I know.
2001-03-08 01:08:39 +00:00
|
|
|
|
2000-11-18 00:55:25 +00:00
|
|
|
#ifdef DNS_RBT_USEHASH
|
2004-03-03 22:40:56 +00:00
|
|
|
static inline void
|
2004-10-25 01:27:54 +00:00
|
|
|
hash_node(dns_rbt_t *rbt, dns_rbtnode_t *node, dns_name_t *name);
|
2000-05-19 04:42:08 +00:00
|
|
|
static inline void
|
2000-10-25 07:21:31 +00:00
|
|
|
unhash_node(dns_rbt_t *rbt, dns_rbtnode_t *node);
|
2000-11-18 00:55:25 +00:00
|
|
|
#else
|
2012-06-20 14:13:12 -05:00
|
|
|
#define hash_node(rbt, node, name)
|
2000-11-18 00:55:25 +00:00
|
|
|
#define unhash_node(rbt, node)
|
|
|
|
#endif
|
1999-01-31 00:52:53 +00:00
|
|
|
|
2000-05-19 04:42:08 +00:00
|
|
|
static inline void
|
|
|
|
rotate_left(dns_rbtnode_t *node, dns_rbtnode_t **rootp);
|
|
|
|
static inline void
|
|
|
|
rotate_right(dns_rbtnode_t *node, dns_rbtnode_t **rootp);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2000-05-19 04:42:08 +00:00
|
|
|
static void
|
2012-06-20 14:13:12 -05:00
|
|
|
addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order,
|
|
|
|
dns_rbtnode_t **rootp);
|
2000-05-19 04:42:08 +00:00
|
|
|
|
|
|
|
static void
|
2012-06-20 14:13:12 -05:00
|
|
|
deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp);
|
|
|
|
|
|
|
|
static void
|
|
|
|
treefix(dns_rbt_t *rbt, dns_rbtnode_t *n, dns_name_t *name,
|
|
|
|
void (*data_fixer)(dns_rbtnode_t *));
|
|
|
|
|
|
|
|
static isc_result_t
|
|
|
|
deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node);
|
|
|
|
|
|
|
|
static void
|
|
|
|
deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, dns_rbtnode_t **nodep);
|
|
|
|
|
|
|
|
static void
|
|
|
|
printnodename(dns_rbtnode_t *node);
|
|
|
|
|
|
|
|
static void
|
|
|
|
freenode(dns_rbt_t *rbt, dns_rbtnode_t **nodep);
|
|
|
|
|
2012-08-24 10:44:26 +10:00
|
|
|
static isc_result_t
|
2012-06-20 14:13:12 -05:00
|
|
|
dns_rbt_zero_header(FILE *file) {
|
|
|
|
/*
|
|
|
|
* Write out a zeroed header as a placeholder. Doing this ensures
|
|
|
|
* that the file will not read while it is partially written, should
|
|
|
|
* writing fail or be interrupted.
|
|
|
|
*/
|
|
|
|
char buffer[HEADER_LENGTH];
|
|
|
|
isc_result_t result;
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
memset(buffer, 0, HEADER_LENGTH);
|
|
|
|
result = isc_stdio_write(buffer, 1, HEADER_LENGTH, file, NULL);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (result);
|
|
|
|
|
|
|
|
result = fflush(file);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (result);
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write out the real header, including NodeDump version information
|
|
|
|
* and the offset of the first node.
|
|
|
|
*
|
|
|
|
* Any information stored in the rbt object itself should be stored
|
|
|
|
* here.
|
|
|
|
*/
|
2012-06-20 23:46:40 +00:00
|
|
|
static isc_result_t
|
2012-06-20 14:13:12 -05:00
|
|
|
write_header(FILE *file, dns_rbt_t *rbt, isc_uint64_t first_node_offset) {
|
|
|
|
file_header_t header;
|
|
|
|
isc_result_t result;
|
2012-10-23 22:04:06 -07:00
|
|
|
long location;
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
if (FILE_VERSION[0] == '\0') {
|
|
|
|
memset(FILE_VERSION, 0, sizeof(FILE_VERSION));
|
|
|
|
snprintf(FILE_VERSION, sizeof(FILE_VERSION),
|
2013-01-24 14:20:48 -08:00
|
|
|
"RBT Image %s %s", dns_major, dns_mapapi);
|
2012-06-20 14:13:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
memset(&header, 0, sizeof(file_header_t));
|
|
|
|
memcpy(header.version1, FILE_VERSION, sizeof(header.version1));
|
|
|
|
memcpy(header.version2, FILE_VERSION, sizeof(header.version2));
|
|
|
|
header.first_node_offset = first_node_offset;
|
|
|
|
header.ptrsize = (isc_uint32_t) sizeof(void *);
|
|
|
|
header.bigendian = (1 == htonl(1)) ? 1 : 0;
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
#ifdef DNS_RDATASET_FIXED
|
|
|
|
header.rdataset_fixed = 1;
|
|
|
|
#else
|
|
|
|
header.rdataset_fixed = 0;
|
2012-06-20 23:46:40 +00:00
|
|
|
#endif
|
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
header.nodecount = rbt->nodecount;
|
|
|
|
|
2012-10-23 22:04:06 -07:00
|
|
|
location = ftell(file);
|
|
|
|
if (location < 0)
|
|
|
|
return (ISC_R_FAILURE);
|
|
|
|
location = dns_rbt_serialize_align(location);
|
|
|
|
CHECK(isc_stdio_seek(file, location, SEEK_SET));
|
2012-06-20 14:13:12 -05:00
|
|
|
CHECK(isc_stdio_write(&header, 1, sizeof(file_header_t), file, NULL));
|
|
|
|
CHECK(fflush(file));
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
/* Ensure we are always at the end of the file. */
|
|
|
|
CHECK(isc_stdio_seek(file, 0, SEEK_END));
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
2012-06-20 23:46:40 +00:00
|
|
|
static isc_result_t
|
2012-06-21 06:59:59 +00:00
|
|
|
serialize_node(FILE *file, dns_rbtnode_t *node, uintptr_t left,
|
|
|
|
uintptr_t right, uintptr_t down, uintptr_t parent,
|
|
|
|
uintptr_t data)
|
2012-06-20 14:13:12 -05:00
|
|
|
{
|
|
|
|
dns_rbtnode_t temp_node;
|
2012-06-21 06:59:59 +00:00
|
|
|
long file_position;
|
2012-06-20 14:13:12 -05:00
|
|
|
unsigned char *node_data;
|
|
|
|
size_t datasize;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
INSIST(node != NULL);
|
|
|
|
|
|
|
|
file_position = ftell(file);
|
2012-10-23 22:04:06 -07:00
|
|
|
if (file_position < 0)
|
|
|
|
return (ISC_R_FAILURE);
|
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
file_position = dns_rbt_serialize_align(file_position);
|
|
|
|
CHECK(isc_stdio_seek(file, file_position, SEEK_SET));
|
|
|
|
|
|
|
|
temp_node = *node;
|
|
|
|
temp_node.down_is_relative = 0;
|
|
|
|
temp_node.left_is_relative = 0;
|
|
|
|
temp_node.right_is_relative = 0;
|
|
|
|
temp_node.parent_is_relative = 0;
|
|
|
|
temp_node.data_is_relative = 0;
|
|
|
|
temp_node.is_mmapped = 1;
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
/*
|
|
|
|
* If the next node is not NULL, calculate the next node's location
|
|
|
|
* in the file. Note that this will have to change when the data
|
|
|
|
* structure changes, and it also assumes that we always write the
|
|
|
|
* nodes out in list order (which we currently do.)
|
|
|
|
*/
|
|
|
|
if (temp_node.parent != NULL) {
|
|
|
|
temp_node.parent = (dns_rbtnode_t *)(parent);
|
|
|
|
temp_node.parent_is_relative = 1;
|
|
|
|
}
|
|
|
|
if (temp_node.left != NULL) {
|
|
|
|
temp_node.left = (dns_rbtnode_t *)(left);
|
|
|
|
temp_node.left_is_relative = 1;
|
|
|
|
}
|
|
|
|
if (temp_node.right != NULL) {
|
|
|
|
temp_node.right = (dns_rbtnode_t *)(right);
|
|
|
|
temp_node.right_is_relative = 1;
|
|
|
|
}
|
|
|
|
if (temp_node.down != NULL) {
|
|
|
|
temp_node.down = (dns_rbtnode_t *)(down);
|
|
|
|
temp_node.down_is_relative = 1;
|
|
|
|
}
|
|
|
|
if (temp_node.data != NULL) {
|
|
|
|
temp_node.data = (dns_rbtnode_t *)(data);
|
|
|
|
temp_node.data_is_relative = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
node_data = (unsigned char *) node + sizeof(dns_rbtnode_t);
|
|
|
|
datasize = NODE_SIZE(node) - sizeof(dns_rbtnode_t);
|
|
|
|
|
|
|
|
CHECK(isc_stdio_write(&temp_node, 1, sizeof(dns_rbtnode_t),
|
|
|
|
file, NULL));
|
|
|
|
|
|
|
|
CHECK(isc_stdio_write(node_data, 1, datasize, file, NULL));
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
return (result);
|
|
|
|
}
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2004-03-04 06:56:41 +00:00
|
|
|
static isc_result_t
|
2012-06-21 06:59:59 +00:00
|
|
|
serialize_nodes(FILE *file, dns_rbtnode_t *node, uintptr_t parent,
|
2012-06-20 14:13:12 -05:00
|
|
|
dns_rbtdatawriter_t datawriter, isc_uint32_t serial,
|
2012-06-21 06:59:59 +00:00
|
|
|
uintptr_t *where)
|
2012-06-20 14:13:12 -05:00
|
|
|
{
|
2012-06-21 06:59:59 +00:00
|
|
|
uintptr_t left = 0, right = 0, down = 0, data = 0;
|
2012-10-23 22:04:06 -07:00
|
|
|
long location = 0;
|
2012-06-20 14:13:12 -05:00
|
|
|
isc_uint64_t offset_adjust;
|
|
|
|
isc_result_t result;
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
if (node == NULL) {
|
|
|
|
if (where != NULL)
|
|
|
|
*where = 0;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reserve space for current node */
|
2012-10-23 22:04:06 -07:00
|
|
|
location = ftell(file);
|
|
|
|
if (location < 0)
|
|
|
|
return (ISC_R_FAILURE);
|
|
|
|
location = dns_rbt_serialize_align(location);
|
|
|
|
CHECK(isc_stdio_seek(file, location, SEEK_SET));
|
|
|
|
location = ftell(file);
|
|
|
|
if (location < 0)
|
|
|
|
return (ISC_R_FAILURE);
|
|
|
|
|
|
|
|
offset_adjust = dns_rbt_serialize_align(location + NODE_SIZE(node));
|
2012-06-20 14:13:12 -05:00
|
|
|
CHECK(isc_stdio_seek(file, offset_adjust, SEEK_SET));
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
/* Serialize the rest of the tree */
|
2012-10-23 22:04:06 -07:00
|
|
|
CHECK(serialize_nodes(file, getleft(node, NULL), location,
|
2012-06-20 14:13:12 -05:00
|
|
|
datawriter, serial, &left));
|
2012-10-23 22:04:06 -07:00
|
|
|
CHECK(serialize_nodes(file, getright(node, NULL), location,
|
2012-06-20 14:13:12 -05:00
|
|
|
datawriter, serial, &right));
|
2012-10-23 22:04:06 -07:00
|
|
|
CHECK(serialize_nodes(file, getdown(node, NULL), location,
|
2012-06-20 14:13:12 -05:00
|
|
|
datawriter, serial, &down));
|
|
|
|
|
|
|
|
if (node->data != NULL) {
|
2012-10-23 22:04:06 -07:00
|
|
|
long ret;
|
2012-10-24 23:46:51 +00:00
|
|
|
|
2012-10-23 22:04:06 -07:00
|
|
|
ret = ftell(file);
|
|
|
|
if (ret < 0)
|
|
|
|
return (ISC_R_FAILURE);
|
|
|
|
ret = dns_rbt_serialize_align(ret);
|
|
|
|
|
|
|
|
CHECK(isc_stdio_seek(file, ret, SEEK_SET));
|
|
|
|
|
|
|
|
ret = ftell(file);
|
|
|
|
if (ret < 0)
|
|
|
|
return (ISC_R_FAILURE);
|
|
|
|
data = ret;
|
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
datawriter(file, node->data, serial);
|
|
|
|
}
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
/* Seek back to reserved space */
|
2012-10-23 22:04:06 -07:00
|
|
|
CHECK(isc_stdio_seek(file, location, SEEK_SET));
|
2012-06-20 14:13:12 -05:00
|
|
|
|
|
|
|
/* Serialize the current node */
|
|
|
|
CHECK(serialize_node(file, node, left, right, down, parent, data));
|
|
|
|
|
|
|
|
/* Ensure we are always at the end of the file. */
|
|
|
|
CHECK(isc_stdio_seek(file, 0, SEEK_END));
|
|
|
|
|
|
|
|
if (where != NULL)
|
2012-10-23 22:04:06 -07:00
|
|
|
*where = location;
|
2012-06-20 14:13:12 -05:00
|
|
|
|
|
|
|
cleanup:
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
2012-06-25 08:14:34 +10:00
|
|
|
long
|
2012-06-21 06:59:59 +00:00
|
|
|
dns_rbt_serialize_align(long target) {
|
|
|
|
long offset = target % 8;
|
2012-06-20 14:13:12 -05:00
|
|
|
|
|
|
|
if (offset == 0)
|
|
|
|
return (target);
|
|
|
|
else
|
|
|
|
return (target + 8 - offset);
|
|
|
|
}
|
|
|
|
|
2012-06-20 23:46:40 +00:00
|
|
|
isc_result_t
|
|
|
|
dns_rbt_serialize_tree(FILE *file, dns_rbt_t *rbt,
|
2012-06-20 14:13:12 -05:00
|
|
|
dns_rbtdatawriter_t datawriter,
|
2012-06-21 06:59:59 +00:00
|
|
|
isc_uint32_t serial, long *offset)
|
2012-06-20 14:13:12 -05:00
|
|
|
{
|
|
|
|
isc_result_t result;
|
|
|
|
long header_position, node_position, end_position;
|
|
|
|
|
|
|
|
REQUIRE(file != NULL);
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
CHECK(isc_file_isplainfilefd(fileno(file)));
|
|
|
|
|
|
|
|
header_position = ftell(file);
|
2012-10-23 22:04:06 -07:00
|
|
|
if (header_position < 0)
|
|
|
|
return (ISC_R_FAILURE);
|
2012-06-20 14:13:12 -05:00
|
|
|
|
|
|
|
/* Write dummy header */
|
|
|
|
CHECK(dns_rbt_zero_header(file));
|
|
|
|
|
|
|
|
/* Serialize nodes */
|
|
|
|
node_position = ftell(file);
|
2012-10-23 22:04:06 -07:00
|
|
|
if (node_position < 0)
|
|
|
|
return (ISC_R_FAILURE);
|
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
CHECK(serialize_nodes(file, rbt->root, 0, datawriter, serial, NULL));
|
|
|
|
end_position = ftell(file);
|
2012-10-23 22:04:06 -07:00
|
|
|
if (end_position < 0)
|
|
|
|
return (ISC_R_FAILURE);
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
if (node_position == end_position) {
|
|
|
|
CHECK(isc_stdio_seek(file, header_position, SEEK_SET));
|
|
|
|
*offset = 0;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
/* Serialize header */
|
|
|
|
CHECK(isc_stdio_seek(file, header_position, SEEK_SET));
|
|
|
|
CHECK(write_header(file, rbt, HEADER_LENGTH));
|
|
|
|
|
|
|
|
/* Ensure we are always at the end of the file. */
|
|
|
|
CHECK(isc_stdio_seek(file, 0, SEEK_END));
|
|
|
|
*offset = dns_rbt_serialize_align(header_position);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
return (result);
|
|
|
|
}
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2004-03-04 06:56:41 +00:00
|
|
|
static void
|
2012-06-20 14:13:12 -05:00
|
|
|
treefix(dns_rbt_t *rbt, dns_rbtnode_t *n, dns_name_t *name,
|
|
|
|
void (*data_fixer)(dns_rbtnode_t *))
|
|
|
|
{
|
|
|
|
isc_result_t result;
|
|
|
|
dns_fixedname_t fixed;
|
|
|
|
dns_name_t nodename, *fullname;
|
|
|
|
|
|
|
|
if (n == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dns_name_init(&nodename, NULL);
|
|
|
|
NODENAME(n, &nodename);
|
|
|
|
|
|
|
|
fullname = &nodename;
|
|
|
|
if (!dns_name_isabsolute(&nodename)) {
|
|
|
|
dns_fixedname_init(&fixed);
|
|
|
|
fullname = dns_fixedname_name(&fixed);
|
|
|
|
result = dns_name_concatenate(&nodename, name, fullname, NULL);
|
|
|
|
INSIST(result == ISC_R_SUCCESS);
|
|
|
|
/* XXX: we need to catch errors better than this */
|
|
|
|
}
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
INSIST(!n->parent_is_relative || n->parent != NULL);
|
|
|
|
INSIST(!n->right_is_relative || n->right != NULL);
|
|
|
|
INSIST(!n->left_is_relative || n->left != NULL);
|
|
|
|
INSIST(!n->down_is_relative || n->down != NULL);
|
|
|
|
INSIST(!n->data_is_relative || n->data != NULL);
|
|
|
|
|
|
|
|
n->right = getright(n, rbt->mmap_location);
|
|
|
|
n->right_is_relative = 0;
|
|
|
|
|
|
|
|
n->left = getleft(n, rbt->mmap_location);
|
|
|
|
n->left_is_relative = 0;
|
|
|
|
|
|
|
|
n->down = getdown(n, rbt->mmap_location);
|
|
|
|
n->down_is_relative = 0;
|
|
|
|
|
|
|
|
n->parent = getparent(n, rbt->mmap_location);
|
|
|
|
n->parent_is_relative = 0;
|
|
|
|
|
|
|
|
n->data = getdata(n, rbt->mmap_location);
|
|
|
|
n->data_is_relative = 0;
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
hash_node(rbt, n, fullname);
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
if (data_fixer != NULL && n->data != NULL)
|
|
|
|
data_fixer(n);
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
if (n->right != NULL)
|
|
|
|
treefix(rbt, n->right, name, data_fixer);
|
|
|
|
if (n->left != NULL)
|
|
|
|
treefix(rbt, n->left, name, data_fixer);
|
|
|
|
if (n->down != NULL)
|
|
|
|
treefix(rbt, n->down, fullname, data_fixer);
|
|
|
|
}
|
|
|
|
|
2012-06-20 23:46:40 +00:00
|
|
|
isc_result_t
|
2012-06-20 14:13:12 -05:00
|
|
|
dns_rbt_deserialize_tree(void *base_address, off_t header_offset,
|
|
|
|
isc_mem_t *mctx,
|
|
|
|
void (*deleter)(void *, void *),
|
|
|
|
void *deleter_arg,
|
|
|
|
void (*data_fixer)(dns_rbtnode_t *),
|
|
|
|
dns_rbtnode_t **originp, dns_rbt_t **rbtp)
|
|
|
|
{
|
|
|
|
isc_result_t result;
|
|
|
|
file_header_t *header;
|
|
|
|
|
|
|
|
REQUIRE(originp == NULL || *originp == NULL);
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
result = dns_rbt_create(mctx, deleter, deleter_arg, rbtp);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (result);
|
|
|
|
|
|
|
|
(*rbtp)->mmap_location = base_address;
|
|
|
|
|
|
|
|
header = (file_header_t *)((char *)base_address + header_offset);
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
#ifdef DNS_RDATASET_FIXED
|
|
|
|
if (header->rdataset_fixed != 1) {
|
|
|
|
return (ISC_R_INVALIDFILE);
|
|
|
|
}
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
#else
|
|
|
|
if (header->rdataset_fixed != 0) {
|
|
|
|
return (ISC_R_INVALIDFILE);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (header->ptrsize != (isc_uint32_t) sizeof(void *)) {
|
|
|
|
return (ISC_R_INVALIDFILE);
|
|
|
|
}
|
|
|
|
if (header->bigendian != (1 == htonl(1)) ? 1 : 0) {
|
|
|
|
return (ISC_R_INVALIDFILE);
|
|
|
|
}
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
/* Copy other data items from the header into our rbt. */
|
|
|
|
(*rbtp)->root = (dns_rbtnode_t *)((char *)base_address +
|
|
|
|
header_offset + header->first_node_offset);
|
|
|
|
(*rbtp)->nodecount = header->nodecount;
|
|
|
|
treefix(*rbtp, (*rbtp)->root, dns_rootname, data_fixer);
|
|
|
|
|
|
|
|
if (originp != NULL)
|
|
|
|
*originp = (*rbtp)->root;
|
|
|
|
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
2004-03-04 06:56:41 +00:00
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
|
|
|
* Initialize a red/black tree of trees.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t
|
2005-10-13 01:19:15 +00:00
|
|
|
dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *),
|
2008-01-22 23:28:04 +00:00
|
|
|
void *deleter_arg, dns_rbt_t **rbtp)
|
1999-02-06 01:27:35 +00:00
|
|
|
{
|
2004-03-03 22:40:56 +00:00
|
|
|
#ifdef DNS_RBT_USEHASH
|
2008-01-22 23:28:04 +00:00
|
|
|
isc_result_t result;
|
2004-03-03 22:40:56 +00:00
|
|
|
#endif
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbt_t *rbt;
|
2007-10-19 17:15:53 +00:00
|
|
|
|
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
REQUIRE(mctx != NULL);
|
|
|
|
REQUIRE(rbtp != NULL && *rbtp == NULL);
|
|
|
|
REQUIRE(deleter == NULL ? deleter_arg == NULL : 1);
|
2007-10-19 17:15:53 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
rbt = (dns_rbt_t *)isc_mem_get(mctx, sizeof(*rbt));
|
|
|
|
if (rbt == NULL)
|
|
|
|
return (ISC_R_NOMEMORY);
|
2007-10-19 17:15:53 +00:00
|
|
|
|
2013-02-20 21:39:05 -08:00
|
|
|
rbt->mctx = NULL;
|
|
|
|
isc_mem_attach(mctx, &rbt->mctx);
|
2008-01-22 23:28:04 +00:00
|
|
|
rbt->data_deleter = deleter;
|
|
|
|
rbt->deleter_arg = deleter_arg;
|
|
|
|
rbt->root = NULL;
|
|
|
|
rbt->nodecount = 0;
|
|
|
|
rbt->hashtable = NULL;
|
|
|
|
rbt->hashsize = 0;
|
2012-06-20 14:13:12 -05:00
|
|
|
rbt->mmap_location = NULL;
|
2007-10-19 17:15:53 +00:00
|
|
|
|
2004-03-03 22:40:56 +00:00
|
|
|
#ifdef DNS_RBT_USEHASH
|
2008-01-22 23:28:04 +00:00
|
|
|
result = inithash(rbt);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
2013-02-20 21:39:05 -08:00
|
|
|
isc_mem_putanddetach(&rbt->mctx, rbt, sizeof(*rbt));
|
2008-01-22 23:28:04 +00:00
|
|
|
return (result);
|
|
|
|
}
|
2004-03-03 22:40:56 +00:00
|
|
|
#endif
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
rbt->magic = RBT_MAGIC;
|
2007-10-19 17:15:53 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
*rbtp = rbt;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-03-12 05:00:32 +00:00
|
|
|
* Deallocate a red/black tree of trees.
|
1999-01-20 10:13:43 +00:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
dns_rbt_destroy(dns_rbt_t **rbtp) {
|
2008-01-22 23:28:04 +00:00
|
|
|
RUNTIME_CHECK(dns_rbt_destroy2(rbtp, 0) == ISC_R_SUCCESS);
|
2004-03-04 06:56:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
isc_result_t
|
|
|
|
dns_rbt_destroy2(dns_rbt_t **rbtp, unsigned int quantum) {
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbt_t *rbt;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
REQUIRE(rbtp != NULL && VALID_RBT(*rbtp));
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
rbt = *rbtp;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
deletetreeflat(rbt, quantum, &rbt->root);
|
2008-01-22 23:28:04 +00:00
|
|
|
if (rbt->root != NULL)
|
|
|
|
return (ISC_R_QUOTA);
|
1999-01-25 15:46:30 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
INSIST(rbt->nodecount == 0);
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
rbt->mmap_location = NULL;
|
2000-08-03 19:46:37 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
if (rbt->hashtable != NULL)
|
|
|
|
isc_mem_put(rbt->mctx, rbt->hashtable,
|
|
|
|
rbt->hashsize * sizeof(dns_rbtnode_t *));
|
2000-10-25 07:21:31 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
rbt->magic = 0;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2013-02-20 21:39:05 -08:00
|
|
|
isc_mem_putanddetach(&rbt->mctx, rbt, sizeof(*rbt));
|
2008-01-22 23:28:04 +00:00
|
|
|
*rbtp = NULL;
|
|
|
|
return (ISC_R_SUCCESS);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
2000-08-03 19:46:37 +00:00
|
|
|
unsigned int
|
|
|
|
dns_rbt_nodecount(dns_rbt_t *rbt) {
|
2008-01-22 23:28:04 +00:00
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
return (rbt->nodecount);
|
2000-08-03 19:46:37 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 10:06:05 -07:00
|
|
|
unsigned int
|
|
|
|
dns_rbt_hashsize(dns_rbt_t *rbt) {
|
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
return (rbt->hashsize);
|
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static inline isc_result_t
|
The ADD_ANCESTOR macro now checks for the required memory, since it is used
in a lot of places now. It _returns_ if it fails to get the necessary memory,
so any function using it should be returning a dns_result_t.
chain_name moved toward start of file, for inlining by findnode.
move_chain_to_last broke out some functionality of dns_rbtnodechain_last
(which now calls it), also for the benefit of findnode.
dns_rbtnodechain_init on the chain used by addnode.
All of the new_foundname concatenation gook was removed from findnode,
which now just sets foundname by using chain_name.
The chain set up by findnode points to the predecessor when the searched
for name is not found.
nodechain_current was changed to take name, origin and node parameters while
returning a dns_result_t. This allows the chain returned by findnode to
be named.
The nodechain_{first,last,prev} use nodechain_current to set their
names and origins. nodechain_next does not need it because it will never
have to do the "set the origin to '.' and remove '.' from names in the
root level" bit. The root level will never be more than one name, and
everything in the megatree is a successor to that node, so 'next' never
reaches the root level.
1999-04-16 16:12:15 +00:00
|
|
|
chain_name(dns_rbtnodechain_t *chain, dns_name_t *name,
|
2008-01-22 23:28:04 +00:00
|
|
|
isc_boolean_t include_chain_end)
|
The ADD_ANCESTOR macro now checks for the required memory, since it is used
in a lot of places now. It _returns_ if it fails to get the necessary memory,
so any function using it should be returning a dns_result_t.
chain_name moved toward start of file, for inlining by findnode.
move_chain_to_last broke out some functionality of dns_rbtnodechain_last
(which now calls it), also for the benefit of findnode.
dns_rbtnodechain_init on the chain used by addnode.
All of the new_foundname concatenation gook was removed from findnode,
which now just sets foundname by using chain_name.
The chain set up by findnode points to the predecessor when the searched
for name is not found.
nodechain_current was changed to take name, origin and node parameters while
returning a dns_result_t. This allows the chain returned by findnode to
be named.
The nodechain_{first,last,prev} use nodechain_current to set their
names and origins. nodechain_next does not need it because it will never
have to do the "set the origin to '.' and remove '.' from names in the
root level" bit. The root level will never be more than one name, and
everything in the megatree is a successor to that node, so 'next' never
reaches the root level.
1999-04-16 16:12:15 +00:00
|
|
|
{
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_name_t nodename;
|
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
dns_name_init(&nodename, NULL);
|
|
|
|
|
|
|
|
if (include_chain_end && chain->end != NULL) {
|
|
|
|
NODENAME(chain->end, &nodename);
|
|
|
|
result = dns_name_copy(&nodename, name, NULL);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (result);
|
|
|
|
} else
|
|
|
|
dns_name_reset(name);
|
|
|
|
|
|
|
|
for (i = (int)chain->level_count - 1; i >= 0; i--) {
|
|
|
|
NODENAME(chain->levels[i], &nodename);
|
|
|
|
result = dns_name_concatenate(name, &nodename, name, NULL);
|
|
|
|
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
return (result);
|
The ADD_ANCESTOR macro now checks for the required memory, since it is used
in a lot of places now. It _returns_ if it fails to get the necessary memory,
so any function using it should be returning a dns_result_t.
chain_name moved toward start of file, for inlining by findnode.
move_chain_to_last broke out some functionality of dns_rbtnodechain_last
(which now calls it), also for the benefit of findnode.
dns_rbtnodechain_init on the chain used by addnode.
All of the new_foundname concatenation gook was removed from findnode,
which now just sets foundname by using chain_name.
The chain set up by findnode points to the predecessor when the searched
for name is not found.
nodechain_current was changed to take name, origin and node parameters while
returning a dns_result_t. This allows the chain returned by findnode to
be named.
The nodechain_{first,last,prev} use nodechain_current to set their
names and origins. nodechain_next does not need it because it will never
have to do the "set the origin to '.' and remove '.' from names in the
root level" bit. The root level will never be more than one name, and
everything in the megatree is a successor to that node, so 'next' never
reaches the root level.
1999-04-16 16:12:15 +00:00
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static inline isc_result_t
|
The ADD_ANCESTOR macro now checks for the required memory, since it is used
in a lot of places now. It _returns_ if it fails to get the necessary memory,
so any function using it should be returning a dns_result_t.
chain_name moved toward start of file, for inlining by findnode.
move_chain_to_last broke out some functionality of dns_rbtnodechain_last
(which now calls it), also for the benefit of findnode.
dns_rbtnodechain_init on the chain used by addnode.
All of the new_foundname concatenation gook was removed from findnode,
which now just sets foundname by using chain_name.
The chain set up by findnode points to the predecessor when the searched
for name is not found.
nodechain_current was changed to take name, origin and node parameters while
returning a dns_result_t. This allows the chain returned by findnode to
be named.
The nodechain_{first,last,prev} use nodechain_current to set their
names and origins. nodechain_next does not need it because it will never
have to do the "set the origin to '.' and remove '.' from names in the
root level" bit. The root level will never be more than one name, and
everything in the megatree is a successor to that node, so 'next' never
reaches the root level.
1999-04-16 16:12:15 +00:00
|
|
|
move_chain_to_last(dns_rbtnodechain_t *chain, dns_rbtnode_t *node) {
|
2008-01-22 23:28:04 +00:00
|
|
|
do {
|
|
|
|
/*
|
|
|
|
* Go as far right and then down as much as possible,
|
|
|
|
* as long as the rightmost node has a down pointer.
|
|
|
|
*/
|
|
|
|
while (RIGHT(node) != NULL)
|
|
|
|
node = RIGHT(node);
|
The ADD_ANCESTOR macro now checks for the required memory, since it is used
in a lot of places now. It _returns_ if it fails to get the necessary memory,
so any function using it should be returning a dns_result_t.
chain_name moved toward start of file, for inlining by findnode.
move_chain_to_last broke out some functionality of dns_rbtnodechain_last
(which now calls it), also for the benefit of findnode.
dns_rbtnodechain_init on the chain used by addnode.
All of the new_foundname concatenation gook was removed from findnode,
which now just sets foundname by using chain_name.
The chain set up by findnode points to the predecessor when the searched
for name is not found.
nodechain_current was changed to take name, origin and node parameters while
returning a dns_result_t. This allows the chain returned by findnode to
be named.
The nodechain_{first,last,prev} use nodechain_current to set their
names and origins. nodechain_next does not need it because it will never
have to do the "set the origin to '.' and remove '.' from names in the
root level" bit. The root level will never be more than one name, and
everything in the megatree is a successor to that node, so 'next' never
reaches the root level.
1999-04-16 16:12:15 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
if (DOWN(node) == NULL)
|
|
|
|
break;
|
The ADD_ANCESTOR macro now checks for the required memory, since it is used
in a lot of places now. It _returns_ if it fails to get the necessary memory,
so any function using it should be returning a dns_result_t.
chain_name moved toward start of file, for inlining by findnode.
move_chain_to_last broke out some functionality of dns_rbtnodechain_last
(which now calls it), also for the benefit of findnode.
dns_rbtnodechain_init on the chain used by addnode.
All of the new_foundname concatenation gook was removed from findnode,
which now just sets foundname by using chain_name.
The chain set up by findnode points to the predecessor when the searched
for name is not found.
nodechain_current was changed to take name, origin and node parameters while
returning a dns_result_t. This allows the chain returned by findnode to
be named.
The nodechain_{first,last,prev} use nodechain_current to set their
names and origins. nodechain_next does not need it because it will never
have to do the "set the origin to '.' and remove '.' from names in the
root level" bit. The root level will never be more than one name, and
everything in the megatree is a successor to that node, so 'next' never
reaches the root level.
1999-04-16 16:12:15 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
ADD_LEVEL(chain, node);
|
|
|
|
node = DOWN(node);
|
|
|
|
} while (1);
|
The ADD_ANCESTOR macro now checks for the required memory, since it is used
in a lot of places now. It _returns_ if it fails to get the necessary memory,
so any function using it should be returning a dns_result_t.
chain_name moved toward start of file, for inlining by findnode.
move_chain_to_last broke out some functionality of dns_rbtnodechain_last
(which now calls it), also for the benefit of findnode.
dns_rbtnodechain_init on the chain used by addnode.
All of the new_foundname concatenation gook was removed from findnode,
which now just sets foundname by using chain_name.
The chain set up by findnode points to the predecessor when the searched
for name is not found.
nodechain_current was changed to take name, origin and node parameters while
returning a dns_result_t. This allows the chain returned by findnode to
be named.
The nodechain_{first,last,prev} use nodechain_current to set their
names and origins. nodechain_next does not need it because it will never
have to do the "set the origin to '.' and remove '.' from names in the
root level" bit. The root level will never be more than one name, and
everything in the megatree is a successor to that node, so 'next' never
reaches the root level.
1999-04-16 16:12:15 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
chain->end = node;
|
The ADD_ANCESTOR macro now checks for the required memory, since it is used
in a lot of places now. It _returns_ if it fails to get the necessary memory,
so any function using it should be returning a dns_result_t.
chain_name moved toward start of file, for inlining by findnode.
move_chain_to_last broke out some functionality of dns_rbtnodechain_last
(which now calls it), also for the benefit of findnode.
dns_rbtnodechain_init on the chain used by addnode.
All of the new_foundname concatenation gook was removed from findnode,
which now just sets foundname by using chain_name.
The chain set up by findnode points to the predecessor when the searched
for name is not found.
nodechain_current was changed to take name, origin and node parameters while
returning a dns_result_t. This allows the chain returned by findnode to
be named.
The nodechain_{first,last,prev} use nodechain_current to set their
names and origins. nodechain_next does not need it because it will never
have to do the "set the origin to '.' and remove '.' from names in the
root level" bit. The root level will never be more than one name, and
everything in the megatree is a successor to that node, so 'next' never
reaches the root level.
1999-04-16 16:12:15 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
The ADD_ANCESTOR macro now checks for the required memory, since it is used
in a lot of places now. It _returns_ if it fails to get the necessary memory,
so any function using it should be returning a dns_result_t.
chain_name moved toward start of file, for inlining by findnode.
move_chain_to_last broke out some functionality of dns_rbtnodechain_last
(which now calls it), also for the benefit of findnode.
dns_rbtnodechain_init on the chain used by addnode.
All of the new_foundname concatenation gook was removed from findnode,
which now just sets foundname by using chain_name.
The chain set up by findnode points to the predecessor when the searched
for name is not found.
nodechain_current was changed to take name, origin and node parameters while
returning a dns_result_t. This allows the chain returned by findnode to
be named.
The nodechain_{first,last,prev} use nodechain_current to set their
names and origins. nodechain_next does not need it because it will never
have to do the "set the origin to '.' and remove '.' from names in the
root level" bit. The root level will never be more than one name, and
everything in the megatree is a successor to that node, so 'next' never
reaches the root level.
1999-04-16 16:12:15 +00:00
|
|
|
}
|
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
/*
|
|
|
|
* Add 'name' to tree, initializing its data pointer with 'data'.
|
|
|
|
*/
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t
|
1999-01-31 00:52:53 +00:00
|
|
|
dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
|
2008-01-22 23:28:04 +00:00
|
|
|
/*
|
|
|
|
* Does this thing have too many variables or what?
|
|
|
|
*/
|
|
|
|
dns_rbtnode_t **root, *parent, *child, *current, *new_current;
|
|
|
|
dns_name_t *add_name, *new_name, current_name, *prefix, *suffix;
|
|
|
|
dns_fixedname_t fixedcopy, fixedprefix, fixedsuffix, fnewname;
|
|
|
|
dns_offsets_t current_offsets;
|
|
|
|
dns_namereln_t compared;
|
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
|
|
dns_rbtnodechain_t chain;
|
|
|
|
unsigned int common_labels;
|
|
|
|
unsigned int nlabels, hlabels;
|
|
|
|
int order;
|
|
|
|
|
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
REQUIRE(dns_name_isabsolute(name));
|
|
|
|
REQUIRE(nodep != NULL && *nodep == NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a copy of the name so the original name structure is
|
|
|
|
* not modified.
|
|
|
|
*/
|
|
|
|
dns_fixedname_init(&fixedcopy);
|
|
|
|
add_name = dns_fixedname_name(&fixedcopy);
|
|
|
|
dns_name_clone(name, add_name);
|
|
|
|
|
|
|
|
if (rbt->root == NULL) {
|
|
|
|
result = create_node(rbt->mctx, add_name, &new_current);
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
rbt->nodecount++;
|
|
|
|
new_current->is_root = 1;
|
|
|
|
rbt->root = new_current;
|
|
|
|
*nodep = new_current;
|
|
|
|
hash_node(rbt, new_current, name);
|
|
|
|
}
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
dns_rbtnodechain_init(&chain, rbt->mctx);
|
|
|
|
|
|
|
|
dns_fixedname_init(&fixedprefix);
|
|
|
|
dns_fixedname_init(&fixedsuffix);
|
|
|
|
prefix = dns_fixedname_name(&fixedprefix);
|
|
|
|
suffix = dns_fixedname_name(&fixedsuffix);
|
|
|
|
|
|
|
|
root = &rbt->root;
|
|
|
|
INSIST(IS_ROOT(*root));
|
|
|
|
parent = NULL;
|
|
|
|
current = NULL;
|
|
|
|
child = *root;
|
|
|
|
dns_name_init(¤t_name, current_offsets);
|
|
|
|
dns_fixedname_init(&fnewname);
|
|
|
|
new_name = dns_fixedname_name(&fnewname);
|
|
|
|
nlabels = dns_name_countlabels(name);
|
|
|
|
hlabels = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
current = child;
|
|
|
|
|
|
|
|
NODENAME(current, ¤t_name);
|
|
|
|
compared = dns_name_fullcompare(add_name, ¤t_name,
|
|
|
|
&order, &common_labels);
|
|
|
|
|
|
|
|
if (compared == dns_namereln_equal) {
|
|
|
|
*nodep = current;
|
|
|
|
result = ISC_R_EXISTS;
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (compared == dns_namereln_none) {
|
|
|
|
|
|
|
|
if (order < 0) {
|
|
|
|
parent = current;
|
|
|
|
child = LEFT(current);
|
|
|
|
|
|
|
|
} else if (order > 0) {
|
|
|
|
parent = current;
|
|
|
|
child = RIGHT(current);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* This name has some suffix in common with the
|
|
|
|
* name at the current node. If the name at
|
|
|
|
* the current node is shorter, that means the
|
|
|
|
* new name should be in a subtree. If the
|
|
|
|
* name at the current node is longer, that means
|
|
|
|
* the down pointer to this tree should point
|
|
|
|
* to a new tree that has the common suffix, and
|
|
|
|
* the non-common parts of these two names should
|
|
|
|
* start a new tree.
|
|
|
|
*/
|
|
|
|
hlabels += common_labels;
|
|
|
|
if (compared == dns_namereln_subdomain) {
|
|
|
|
/*
|
|
|
|
* All of the existing labels are in common,
|
|
|
|
* so the new name is in a subtree.
|
|
|
|
* Whack off the common labels for the
|
|
|
|
* not-in-common part to be searched for
|
|
|
|
* in the next level.
|
|
|
|
*/
|
|
|
|
dns_name_split(add_name, common_labels,
|
|
|
|
add_name, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Follow the down pointer (possibly NULL).
|
|
|
|
*/
|
|
|
|
root = &DOWN(current);
|
|
|
|
|
|
|
|
INSIST(*root == NULL ||
|
|
|
|
(IS_ROOT(*root) &&
|
|
|
|
PARENT(*root) == current));
|
|
|
|
|
|
|
|
parent = NULL;
|
|
|
|
child = DOWN(current);
|
|
|
|
ADD_LEVEL(&chain, current);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* The number of labels in common is fewer
|
|
|
|
* than the number of labels at the current
|
|
|
|
* node, so the current node must be adjusted
|
|
|
|
* to have just the common suffix, and a down
|
|
|
|
* pointer made to a new tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
INSIST(compared == dns_namereln_commonancestor
|
|
|
|
|| compared == dns_namereln_contains);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensure the number of levels in the tree
|
|
|
|
* does not exceed the number of logical
|
|
|
|
* levels allowed by DNSSEC.
|
|
|
|
*
|
|
|
|
* XXXDCL need a better error result?
|
|
|
|
*
|
|
|
|
* XXXDCL Since chain ancestors were removed,
|
2012-06-20 14:13:12 -05:00
|
|
|
* no longer used by addonlevel(),
|
2008-01-22 23:28:04 +00:00
|
|
|
* this is the only real use of chains in the
|
|
|
|
* function. It could be done instead with
|
|
|
|
* a simple integer variable, but I am pressed
|
|
|
|
* for time.
|
|
|
|
*/
|
|
|
|
if (chain.level_count ==
|
|
|
|
(sizeof(chain.levels) /
|
|
|
|
sizeof(*chain.levels))) {
|
|
|
|
result = ISC_R_NOSPACE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Split the name into two parts, a prefix
|
|
|
|
* which is the not-in-common parts of the
|
|
|
|
* two names and a suffix that is the common
|
|
|
|
* parts of them.
|
|
|
|
*/
|
|
|
|
dns_name_split(¤t_name, common_labels,
|
|
|
|
prefix, suffix);
|
|
|
|
result = create_node(rbt->mctx, suffix,
|
|
|
|
&new_current);
|
|
|
|
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reproduce the tree attributes of the
|
|
|
|
* current node.
|
|
|
|
*/
|
|
|
|
new_current->is_root = current->is_root;
|
2009-10-27 04:46:58 +00:00
|
|
|
if (current->nsec == DNS_RBT_NSEC_HAS_NSEC)
|
|
|
|
new_current->nsec = DNS_RBT_NSEC_NORMAL;
|
|
|
|
else
|
|
|
|
new_current->nsec = current->nsec;
|
2008-01-22 23:28:04 +00:00
|
|
|
PARENT(new_current) = PARENT(current);
|
|
|
|
LEFT(new_current) = LEFT(current);
|
|
|
|
RIGHT(new_current) = RIGHT(current);
|
|
|
|
COLOR(new_current) = COLOR(current);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fix pointers that were to the current node.
|
|
|
|
*/
|
|
|
|
if (parent != NULL) {
|
|
|
|
if (LEFT(parent) == current)
|
|
|
|
LEFT(parent) = new_current;
|
|
|
|
else
|
|
|
|
RIGHT(parent) = new_current;
|
|
|
|
}
|
|
|
|
if (LEFT(new_current) != NULL)
|
|
|
|
PARENT(LEFT(new_current)) =
|
|
|
|
new_current;
|
|
|
|
if (RIGHT(new_current) != NULL)
|
|
|
|
PARENT(RIGHT(new_current)) =
|
|
|
|
new_current;
|
|
|
|
if (*root == current)
|
|
|
|
*root = new_current;
|
|
|
|
|
|
|
|
NAMELEN(current) = prefix->length;
|
|
|
|
OFFSETLEN(current) = prefix->labels;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up the new root of the next level.
|
|
|
|
* By definition it will not be the top
|
|
|
|
* level tree, so clear DNS_NAMEATTR_ABSOLUTE.
|
|
|
|
*/
|
|
|
|
current->is_root = 1;
|
|
|
|
PARENT(current) = new_current;
|
|
|
|
DOWN(new_current) = current;
|
|
|
|
root = &DOWN(new_current);
|
|
|
|
|
|
|
|
ADD_LEVEL(&chain, new_current);
|
|
|
|
|
|
|
|
LEFT(current) = NULL;
|
|
|
|
RIGHT(current) = NULL;
|
|
|
|
|
|
|
|
MAKE_BLACK(current);
|
|
|
|
ATTRS(current) &= ~DNS_NAMEATTR_ABSOLUTE;
|
|
|
|
|
|
|
|
rbt->nodecount++;
|
|
|
|
dns_name_getlabelsequence(name,
|
|
|
|
nlabels - hlabels,
|
|
|
|
hlabels, new_name);
|
|
|
|
hash_node(rbt, new_current, new_name);
|
|
|
|
|
|
|
|
if (common_labels ==
|
|
|
|
dns_name_countlabels(add_name)) {
|
|
|
|
/*
|
|
|
|
* The name has been added by pushing
|
|
|
|
* the not-in-common parts down to
|
|
|
|
* a new level.
|
|
|
|
*/
|
|
|
|
*nodep = new_current;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* The current node has no data,
|
|
|
|
* because it is just a placeholder.
|
|
|
|
* Its data pointer is already NULL
|
|
|
|
* from create_node()), so there's
|
|
|
|
* nothing more to do to it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The not-in-common parts of the new
|
|
|
|
* name will be inserted into the new
|
|
|
|
* level following this loop (unless
|
|
|
|
* result != ISC_R_SUCCESS, which
|
|
|
|
* is tested after the loop ends).
|
|
|
|
*/
|
|
|
|
dns_name_split(add_name, common_labels,
|
|
|
|
add_name, NULL);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (child != NULL);
|
|
|
|
|
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
result = create_node(rbt->mctx, add_name, &new_current);
|
|
|
|
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
2012-06-20 14:13:12 -05:00
|
|
|
addonlevel(new_current, current, order, root);
|
2008-01-22 23:28:04 +00:00
|
|
|
rbt->nodecount++;
|
|
|
|
*nodep = new_current;
|
|
|
|
hash_node(rbt, new_current, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (result);
|
1999-01-31 00:52:53 +00:00
|
|
|
}
|
|
|
|
|
1999-03-12 05:00:32 +00:00
|
|
|
/*
|
|
|
|
* Add a name to the tree of trees, associating it with some data.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t
|
1999-01-31 00:52:53 +00:00
|
|
|
dns_rbt_addname(dns_rbt_t *rbt, dns_name_t *name, void *data) {
|
2008-01-22 23:28:04 +00:00
|
|
|
isc_result_t result;
|
|
|
|
dns_rbtnode_t *node;
|
1999-01-31 00:52:53 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
REQUIRE(dns_name_isabsolute(name));
|
1999-01-31 00:52:53 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
node = NULL;
|
1999-01-31 00:52:53 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
result = dns_rbt_addnode(rbt, name, &node);
|
1999-03-12 05:00:32 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
/*
|
|
|
|
* dns_rbt_addnode will report the node exists even when
|
|
|
|
* it does not have data associated with it, but the
|
|
|
|
* dns_rbt_*name functions all behave depending on whether
|
|
|
|
* there is data associated with a node.
|
|
|
|
*/
|
|
|
|
if (result == ISC_R_SUCCESS ||
|
|
|
|
(result == ISC_R_EXISTS && DATA(node) == NULL)) {
|
|
|
|
DATA(node) = data;
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
}
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
return (result);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the node for "name" in the tree of trees.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t
|
1999-03-16 16:10:36 +00:00
|
|
|
dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnode_t **node, dns_rbtnodechain_t *chain,
|
|
|
|
unsigned int options, dns_rbtfindcallback_t callback,
|
|
|
|
void *callback_arg)
|
1999-03-04 21:03:29 +00:00
|
|
|
{
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnode_t *current, *last_compared, *current_root;
|
|
|
|
dns_rbtnodechain_t localchain;
|
|
|
|
dns_name_t *search_name, current_name, *callback_name;
|
|
|
|
dns_fixedname_t fixedcallbackname, fixedsearchname;
|
|
|
|
dns_namereln_t compared;
|
|
|
|
isc_result_t result, saved_result;
|
|
|
|
unsigned int common_labels;
|
|
|
|
unsigned int hlabels = 0;
|
|
|
|
int order;
|
|
|
|
|
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
REQUIRE(dns_name_isabsolute(name));
|
|
|
|
REQUIRE(node != NULL && *node == NULL);
|
|
|
|
REQUIRE((options & (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR))
|
|
|
|
!= (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there is a chain it needs to appear to be in a sane state,
|
|
|
|
* otherwise a chain is still needed to generate foundname and
|
|
|
|
* callback_name.
|
|
|
|
*/
|
|
|
|
if (chain == NULL) {
|
|
|
|
options |= DNS_RBTFIND_NOPREDECESSOR;
|
|
|
|
chain = &localchain;
|
|
|
|
dns_rbtnodechain_init(chain, rbt->mctx);
|
|
|
|
} else
|
|
|
|
dns_rbtnodechain_reset(chain);
|
|
|
|
|
|
|
|
if (rbt->root == NULL)
|
|
|
|
return (ISC_R_NOTFOUND);
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* Appease GCC about variables it incorrectly thinks are
|
|
|
|
* possibly used uninitialized.
|
|
|
|
*/
|
|
|
|
compared = dns_namereln_none;
|
|
|
|
last_compared = NULL;
|
2011-03-11 06:11:27 +00:00
|
|
|
order = 0;
|
2008-01-22 23:28:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dns_fixedname_init(&fixedcallbackname);
|
|
|
|
callback_name = dns_fixedname_name(&fixedcallbackname);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* search_name is the name segment being sought in each tree level.
|
|
|
|
* By using a fixedname, the search_name will definitely have offsets
|
|
|
|
* for use by any splitting.
|
|
|
|
* By using dns_name_clone, no name data should be copied thanks to
|
|
|
|
* the lack of bitstring labels.
|
|
|
|
*/
|
|
|
|
dns_fixedname_init(&fixedsearchname);
|
|
|
|
search_name = dns_fixedname_name(&fixedsearchname);
|
|
|
|
dns_name_clone(name, search_name);
|
|
|
|
|
|
|
|
dns_name_init(¤t_name, NULL);
|
|
|
|
|
|
|
|
saved_result = ISC_R_SUCCESS;
|
|
|
|
current = rbt->root;
|
|
|
|
current_root = rbt->root;
|
|
|
|
|
|
|
|
while (current != NULL) {
|
|
|
|
NODENAME(current, ¤t_name);
|
|
|
|
compared = dns_name_fullcompare(search_name, ¤t_name,
|
|
|
|
&order, &common_labels);
|
|
|
|
last_compared = current;
|
|
|
|
|
|
|
|
if (compared == dns_namereln_equal)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (compared == dns_namereln_none) {
|
2000-11-18 00:55:25 +00:00
|
|
|
#ifdef DNS_RBT_USEHASH
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_name_t hash_name;
|
|
|
|
dns_rbtnode_t *hnode;
|
|
|
|
dns_rbtnode_t *up_current;
|
|
|
|
unsigned int nlabels;
|
|
|
|
unsigned int tlabels = 1;
|
|
|
|
unsigned int hash;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there is no hash table, hashing can't be done.
|
|
|
|
*/
|
|
|
|
if (rbt->hashtable == NULL)
|
|
|
|
goto nohash;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The case of current != current_root, that
|
|
|
|
* means a left or right pointer was followed,
|
|
|
|
* only happens when the algorithm fell through to
|
|
|
|
* the traditional binary search because of a
|
|
|
|
* bitstring label. Since we dropped the bitstring
|
|
|
|
* support, this should not happen.
|
|
|
|
*/
|
|
|
|
INSIST(current == current_root);
|
|
|
|
|
|
|
|
nlabels = dns_name_countlabels(search_name);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* current_root is the root of the current level, so
|
|
|
|
* it's parent is the same as it's "up" pointer.
|
|
|
|
*/
|
|
|
|
up_current = PARENT(current_root);
|
|
|
|
dns_name_init(&hash_name, NULL);
|
|
|
|
|
|
|
|
hashagain:
|
|
|
|
/*
|
|
|
|
* Hash includes tail.
|
|
|
|
*/
|
|
|
|
dns_name_getlabelsequence(name,
|
|
|
|
nlabels - tlabels,
|
|
|
|
hlabels + tlabels,
|
|
|
|
&hash_name);
|
|
|
|
hash = dns_name_fullhash(&hash_name, ISC_FALSE);
|
|
|
|
dns_name_getlabelsequence(search_name,
|
|
|
|
nlabels - tlabels,
|
|
|
|
tlabels, &hash_name);
|
|
|
|
|
|
|
|
for (hnode = rbt->hashtable[hash % rbt->hashsize];
|
|
|
|
hnode != NULL;
|
|
|
|
hnode = hnode->hashnext)
|
|
|
|
{
|
|
|
|
dns_name_t hnode_name;
|
|
|
|
|
|
|
|
if (hash != HASHVAL(hnode))
|
|
|
|
continue;
|
|
|
|
if (find_up(hnode) != up_current)
|
|
|
|
continue;
|
|
|
|
dns_name_init(&hnode_name, NULL);
|
|
|
|
NODENAME(hnode, &hnode_name);
|
|
|
|
if (dns_name_equal(&hnode_name, &hash_name))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hnode != NULL) {
|
|
|
|
current = hnode;
|
|
|
|
/*
|
|
|
|
* This is an optimization. If hashing found
|
|
|
|
* the right node, the next call to
|
|
|
|
* dns_name_fullcompare() would obviously
|
|
|
|
* return _equal or _subdomain. Determine
|
|
|
|
* which of those would be the case by
|
|
|
|
* checking if the full name was hashed. Then
|
|
|
|
* make it look like dns_name_fullcompare
|
|
|
|
* was called and jump to the right place.
|
|
|
|
*/
|
|
|
|
if (tlabels == nlabels) {
|
|
|
|
compared = dns_namereln_equal;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
common_labels = tlabels;
|
|
|
|
compared = dns_namereln_subdomain;
|
|
|
|
goto subdomain;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tlabels++ < nlabels)
|
|
|
|
goto hashagain;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* All of the labels have been tried against the hash
|
|
|
|
* table. Since we dropped the support of bitstring
|
|
|
|
* labels, the name isn't in the table.
|
|
|
|
*/
|
|
|
|
current = NULL;
|
|
|
|
continue;
|
|
|
|
|
|
|
|
nohash:
|
2000-11-18 00:55:25 +00:00
|
|
|
#endif /* DNS_RBT_USEHASH */
|
2008-01-22 23:28:04 +00:00
|
|
|
/*
|
|
|
|
* Standard binary search tree movement.
|
|
|
|
*/
|
|
|
|
if (order < 0)
|
|
|
|
current = LEFT(current);
|
|
|
|
else
|
|
|
|
current = RIGHT(current);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* The names have some common suffix labels.
|
|
|
|
*
|
|
|
|
* If the number in common are equal in length to
|
|
|
|
* the current node's name length, then follow the
|
|
|
|
* down pointer and search in the new tree.
|
|
|
|
*/
|
|
|
|
if (compared == dns_namereln_subdomain) {
|
2012-06-20 14:13:12 -05:00
|
|
|
#ifdef DNS_RBT_USEHASH
|
2008-01-22 23:28:04 +00:00
|
|
|
subdomain:
|
2012-06-20 14:13:12 -05:00
|
|
|
#endif
|
2008-01-22 23:28:04 +00:00
|
|
|
/*
|
|
|
|
* Whack off the current node's common parts
|
|
|
|
* for the name to search in the next level.
|
|
|
|
*/
|
|
|
|
dns_name_split(search_name, common_labels,
|
|
|
|
search_name, NULL);
|
|
|
|
hlabels += common_labels;
|
|
|
|
/*
|
|
|
|
* This might be the closest enclosing name.
|
|
|
|
*/
|
|
|
|
if (DATA(current) != NULL ||
|
|
|
|
(options & DNS_RBTFIND_EMPTYDATA) != 0)
|
|
|
|
*node = current;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Point the chain to the next level. This
|
|
|
|
* needs to be done before 'current' is pointed
|
|
|
|
* there because the callback in the next
|
|
|
|
* block of code needs the current 'current',
|
|
|
|
* but in the event the callback requests that
|
|
|
|
* the search be stopped then the
|
|
|
|
* DNS_R_PARTIALMATCH code at the end of this
|
|
|
|
* function needs the chain pointed to the
|
|
|
|
* next level.
|
|
|
|
*/
|
|
|
|
ADD_LEVEL(chain, current);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The caller may want to interrupt the
|
|
|
|
* downward search when certain special nodes
|
|
|
|
* are traversed. If this is a special node,
|
|
|
|
* the callback is used to learn what the
|
|
|
|
* caller wants to do.
|
|
|
|
*/
|
|
|
|
if (callback != NULL &&
|
|
|
|
FINDCALLBACK(current)) {
|
|
|
|
result = chain_name(chain,
|
|
|
|
callback_name,
|
|
|
|
ISC_FALSE);
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
dns_rbtnodechain_reset(chain);
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
result = (callback)(current,
|
|
|
|
callback_name,
|
|
|
|
callback_arg);
|
|
|
|
if (result != DNS_R_CONTINUE) {
|
|
|
|
saved_result = result;
|
|
|
|
/*
|
|
|
|
* Treat this node as if it
|
|
|
|
* had no down pointer.
|
|
|
|
*/
|
|
|
|
current = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Finally, head to the next tree level.
|
|
|
|
*/
|
|
|
|
current = DOWN(current);
|
|
|
|
current_root = current;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Though there are labels in common, the
|
|
|
|
* entire name at this node is not common
|
|
|
|
* with the search name so the search
|
|
|
|
* name does not exist in the tree.
|
|
|
|
*/
|
|
|
|
INSIST(compared == dns_namereln_commonancestor
|
|
|
|
|| compared == dns_namereln_contains);
|
|
|
|
|
|
|
|
current = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If current is not NULL, NOEXACT is not disallowing exact matches,
|
|
|
|
* and either the node has data or an empty node is ok, return
|
|
|
|
* ISC_R_SUCCESS to indicate an exact match.
|
|
|
|
*/
|
|
|
|
if (current != NULL && (options & DNS_RBTFIND_NOEXACT) == 0 &&
|
|
|
|
(DATA(current) != NULL ||
|
|
|
|
(options & DNS_RBTFIND_EMPTYDATA) != 0)) {
|
|
|
|
/*
|
|
|
|
* Found an exact match.
|
|
|
|
*/
|
|
|
|
chain->end = current;
|
|
|
|
chain->level_matches = chain->level_count;
|
|
|
|
|
|
|
|
if (foundname != NULL)
|
|
|
|
result = chain_name(chain, foundname, ISC_TRUE);
|
|
|
|
else
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
*node = current;
|
|
|
|
result = saved_result;
|
|
|
|
} else
|
|
|
|
*node = NULL;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Did not find an exact match (or did not want one).
|
|
|
|
*/
|
|
|
|
if (*node != NULL) {
|
|
|
|
/*
|
|
|
|
* ... but found a partially matching superdomain.
|
|
|
|
* Unwind the chain to the partial match node
|
|
|
|
* to set level_matches to the level above the node,
|
|
|
|
* and then to derive the name.
|
|
|
|
*
|
|
|
|
* chain->level_count is guaranteed to be at least 1
|
|
|
|
* here because by definition of finding a superdomain,
|
|
|
|
* the chain is pointed to at least the first subtree.
|
|
|
|
*/
|
|
|
|
chain->level_matches = chain->level_count - 1;
|
|
|
|
|
|
|
|
while (chain->levels[chain->level_matches] != *node) {
|
|
|
|
INSIST(chain->level_matches > 0);
|
|
|
|
chain->level_matches--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (foundname != NULL) {
|
|
|
|
unsigned int saved_count = chain->level_count;
|
|
|
|
|
|
|
|
chain->level_count = chain->level_matches + 1;
|
|
|
|
|
|
|
|
result = chain_name(chain, foundname,
|
|
|
|
ISC_FALSE);
|
|
|
|
|
|
|
|
chain->level_count = saved_count;
|
|
|
|
} else
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
|
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
result = DNS_R_PARTIALMATCH;
|
|
|
|
|
|
|
|
} else
|
|
|
|
result = ISC_R_NOTFOUND;
|
|
|
|
|
|
|
|
if (current != NULL) {
|
|
|
|
/*
|
|
|
|
* There was an exact match but either
|
|
|
|
* DNS_RBTFIND_NOEXACT was set, or
|
|
|
|
* DNS_RBTFIND_EMPTYDATA was set and the node had no
|
|
|
|
* data. A policy decision was made to set the
|
|
|
|
* chain to the exact match, but this is subject
|
|
|
|
* to change if it becomes apparent that something
|
|
|
|
* else would be more useful. It is important that
|
|
|
|
* this case is handled here, because the predecessor
|
|
|
|
* setting code below assumes the match was not exact.
|
|
|
|
*/
|
|
|
|
INSIST(((options & DNS_RBTFIND_NOEXACT) != 0) ||
|
|
|
|
((options & DNS_RBTFIND_EMPTYDATA) == 0 &&
|
|
|
|
DATA(current) == NULL));
|
|
|
|
chain->end = current;
|
|
|
|
|
|
|
|
} else if ((options & DNS_RBTFIND_NOPREDECESSOR) != 0) {
|
|
|
|
/*
|
|
|
|
* Ensure the chain points nowhere.
|
|
|
|
*/
|
|
|
|
chain->end = NULL;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Since there was no exact match, the chain argument
|
|
|
|
* needs to be pointed at the DNSSEC predecessor of
|
|
|
|
* the search name.
|
|
|
|
*/
|
|
|
|
if (compared == dns_namereln_subdomain) {
|
|
|
|
/*
|
|
|
|
* Attempted to follow a down pointer that was
|
|
|
|
* NULL, which means the searched for name was
|
|
|
|
* a subdomain of a terminal name in the tree.
|
|
|
|
* Since there are no existing subdomains to
|
|
|
|
* order against, the terminal name is the
|
|
|
|
* predecessor.
|
|
|
|
*/
|
|
|
|
INSIST(chain->level_count > 0);
|
|
|
|
INSIST(chain->level_matches <
|
|
|
|
chain->level_count);
|
|
|
|
chain->end =
|
|
|
|
chain->levels[--chain->level_count];
|
|
|
|
|
|
|
|
} else {
|
|
|
|
isc_result_t result2;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Point current to the node that stopped
|
|
|
|
* the search.
|
|
|
|
*
|
|
|
|
* With the hashing modification that has been
|
|
|
|
* added to the algorithm, the stop node of a
|
|
|
|
* standard binary search is not known. So it
|
|
|
|
* has to be found. There is probably a more
|
|
|
|
* clever way of doing this.
|
|
|
|
*
|
|
|
|
* The assignment of current to NULL when
|
|
|
|
* the relationship is *not* dns_namereln_none,
|
|
|
|
* even though it later gets set to the same
|
|
|
|
* last_compared anyway, is simply to not push
|
|
|
|
* the while loop in one more level of
|
|
|
|
* indentation.
|
|
|
|
*/
|
|
|
|
if (compared == dns_namereln_none)
|
|
|
|
current = last_compared;
|
|
|
|
else
|
|
|
|
current = NULL;
|
|
|
|
|
|
|
|
while (current != NULL) {
|
|
|
|
NODENAME(current, ¤t_name);
|
|
|
|
compared = dns_name_fullcompare(
|
|
|
|
search_name,
|
|
|
|
¤t_name,
|
|
|
|
&order,
|
|
|
|
&common_labels);
|
2011-03-11 06:11:27 +00:00
|
|
|
POST(compared);
|
2008-01-22 23:28:04 +00:00
|
|
|
|
|
|
|
last_compared = current;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Standard binary search movement.
|
|
|
|
*/
|
|
|
|
if (order < 0)
|
|
|
|
current = LEFT(current);
|
|
|
|
else
|
|
|
|
current = RIGHT(current);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
current = last_compared;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reached a point within a level tree that
|
|
|
|
* positively indicates the name is not
|
|
|
|
* present, but the stop node could be either
|
|
|
|
* less than the desired name (order > 0) or
|
|
|
|
* greater than the desired name (order < 0).
|
|
|
|
*
|
|
|
|
* If the stop node is less, it is not
|
|
|
|
* necessarily the predecessor. If the stop
|
|
|
|
* node has a down pointer, then the real
|
|
|
|
* predecessor is at the end of a level below
|
|
|
|
* (not necessarily the next level).
|
|
|
|
* Move down levels until the rightmost node
|
|
|
|
* does not have a down pointer.
|
|
|
|
*
|
|
|
|
* When the stop node is greater, it is
|
|
|
|
* the successor. All the logic for finding
|
|
|
|
* the predecessor is handily encapsulated
|
|
|
|
* in dns_rbtnodechain_prev. In the event
|
|
|
|
* that the search name is less than anything
|
|
|
|
* else in the tree, the chain is reset.
|
|
|
|
* XXX DCL What is the best way for the caller
|
|
|
|
* to know that the search name has
|
|
|
|
* no predecessor?
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
if (order > 0) {
|
|
|
|
if (DOWN(current) != NULL) {
|
|
|
|
ADD_LEVEL(chain, current);
|
|
|
|
|
|
|
|
result2 =
|
|
|
|
move_chain_to_last(chain,
|
|
|
|
DOWN(current));
|
|
|
|
|
|
|
|
if (result2 != ISC_R_SUCCESS)
|
|
|
|
result = result2;
|
|
|
|
} else
|
|
|
|
/*
|
|
|
|
* Ah, the pure and simple
|
|
|
|
* case. The stop node is the
|
|
|
|
* predecessor.
|
|
|
|
*/
|
|
|
|
chain->end = current;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
INSIST(order < 0);
|
|
|
|
|
|
|
|
chain->end = current;
|
|
|
|
|
|
|
|
result2 = dns_rbtnodechain_prev(chain,
|
|
|
|
NULL,
|
|
|
|
NULL);
|
|
|
|
if (result2 == ISC_R_SUCCESS ||
|
|
|
|
result2 == DNS_R_NEWORIGIN)
|
|
|
|
; /* Nothing. */
|
|
|
|
else if (result2 == ISC_R_NOMORE)
|
|
|
|
/*
|
|
|
|
* There is no predecessor.
|
|
|
|
*/
|
|
|
|
dns_rbtnodechain_reset(chain);
|
|
|
|
else
|
|
|
|
result = result2;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ENSURE(*node == NULL || DNS_RBTNODE_VALID(*node));
|
|
|
|
|
|
|
|
return (result);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-03-12 05:00:32 +00:00
|
|
|
/*
|
|
|
|
* Get the data pointer associated with 'name'.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t
|
2000-04-19 18:21:24 +00:00
|
|
|
dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name, unsigned int options,
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_name_t *foundname, void **data) {
|
|
|
|
dns_rbtnode_t *node = NULL;
|
|
|
|
isc_result_t result;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
REQUIRE(data != NULL && *data == NULL);
|
1999-01-27 01:48:55 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
result = dns_rbt_findnode(rbt, name, foundname, &node, NULL,
|
|
|
|
options, NULL, NULL);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
if (node != NULL &&
|
|
|
|
(DATA(node) != NULL || (options & DNS_RBTFIND_EMPTYDATA) != 0))
|
|
|
|
*data = DATA(node);
|
|
|
|
else
|
|
|
|
result = ISC_R_NOTFOUND;
|
2000-08-01 01:33:37 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
return (result);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete a name from the tree of trees.
|
|
|
|
*/
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t
|
1999-01-31 00:52:53 +00:00
|
|
|
dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) {
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnode_t *node = NULL;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
REQUIRE(dns_name_isabsolute(name));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First, find the node.
|
|
|
|
*
|
|
|
|
* When searching, the name might not have an exact match:
|
|
|
|
* consider a.b.a.com, b.b.a.com and c.b.a.com as the only
|
|
|
|
* elements of a tree, which would make layer 1 a single
|
|
|
|
* node tree of "b.a.com" and layer 2 a three node tree of
|
|
|
|
* a, b, and c. Deleting a.com would find only a partial depth
|
|
|
|
* match in the first layer. Should it be a requirement that
|
|
|
|
* that the name to be deleted have data? For now, it is.
|
|
|
|
*
|
|
|
|
* ->dirty, ->locknum and ->references are ignored; they are
|
|
|
|
* solely the province of rbtdb.c.
|
|
|
|
*/
|
|
|
|
result = dns_rbt_findnode(rbt, name, NULL, &node, NULL,
|
|
|
|
DNS_RBTFIND_NOOPTIONS, NULL, NULL);
|
|
|
|
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
if (DATA(node) != NULL)
|
|
|
|
result = dns_rbt_deletenode(rbt, node, recurse);
|
|
|
|
else
|
|
|
|
result = ISC_R_NOTFOUND;
|
|
|
|
|
|
|
|
} else if (result == DNS_R_PARTIALMATCH)
|
|
|
|
result = ISC_R_NOTFOUND;
|
|
|
|
|
|
|
|
return (result);
|
1999-01-31 18:43:57 +00:00
|
|
|
}
|
|
|
|
|
1999-03-12 05:00:32 +00:00
|
|
|
/*
|
join_nodes is removed; node joining is no longer done at any time, because
it corrupts active chains and can change the data at a node which
someone might be using. (In the latter case, though the full name from
the root was still intact, and the ->data member was not altered, other
values like the partial name ndata and offsets could change.)
New functions dns_rbt_fullnamefromnode() and dns_rbt_formatnodename(),
for getting a dns_name_t and filling a char buffer, respectively, with
the complete name of a node from the root.
rbtnode.is_root was being treated as isc_boolean_t even though it was bitfield
of width 1. This worked fine, but wasn't consistent with ISC style, and
would have puked if someone decided to change isc_true from 1 (unlikely
as that is). Yeah, anal, I know.
2001-03-08 01:08:39 +00:00
|
|
|
* Remove a node from the tree of trees.
|
|
|
|
*
|
|
|
|
* NOTE WELL: deletion is *not* symmetric with addition; that is, reversing
|
|
|
|
* a sequence of additions to be deletions will not generally get the
|
|
|
|
* tree back to the state it started in. For example, if the addition
|
|
|
|
* of "b.c" caused the node "a.b.c" to be split, pushing "a" to its own level,
|
|
|
|
* then the subsequent deletion of "b.c" will not cause "a" to be pulled up,
|
|
|
|
* restoring "a.b.c". The RBT *used* to do this kind of rejoining, but it
|
|
|
|
* turned out to be a bad idea because it could corrupt an active nodechain
|
|
|
|
* that had "b.c" as one of its levels -- and the RBT has no idea what
|
|
|
|
* nodechains are in use by callers, so it can't even *try* to helpfully
|
|
|
|
* fix them up (which would probably be doomed to failure anyway).
|
|
|
|
*
|
|
|
|
* Similarly, it is possible to leave the tree in a state where a supposedly
|
|
|
|
* deleted node still exists. The first case of this is obvious; take
|
|
|
|
* the tree which has "b.c" on one level, pointing to "a". Now deleted "b.c".
|
|
|
|
* It was just established in the previous paragraph why we can't pull "a"
|
|
|
|
* back up to its parent level. But what happens when "a" then gets deleted?
|
|
|
|
* "b.c" is left hanging around without data or children. This condition
|
|
|
|
* is actually pretty easy to detect, but ... should it really be removed?
|
|
|
|
* Is a chain pointing to it? An iterator? Who knows! (Note that the
|
2001-05-31 11:03:33 +00:00
|
|
|
* references structure member cannot be looked at because it is private to
|
|
|
|
* rbtdb.) This is ugly and makes me unhappy, but after hours of trying to
|
|
|
|
* make it more aesthetically proper and getting nowhere, this is the way it
|
|
|
|
* is going to stay until such time as it proves to be a *real* problem.
|
join_nodes is removed; node joining is no longer done at any time, because
it corrupts active chains and can change the data at a node which
someone might be using. (In the latter case, though the full name from
the root was still intact, and the ->data member was not altered, other
values like the partial name ndata and offsets could change.)
New functions dns_rbt_fullnamefromnode() and dns_rbt_formatnodename(),
for getting a dns_name_t and filling a char buffer, respectively, with
the complete name of a node from the root.
rbtnode.is_root was being treated as isc_boolean_t even though it was bitfield
of width 1. This worked fine, but wasn't consistent with ISC style, and
would have puked if someone decided to change isc_true from 1 (unlikely
as that is). Yeah, anal, I know.
2001-03-08 01:08:39 +00:00
|
|
|
*
|
|
|
|
* Finally, for reference, note that the original routine that did node
|
|
|
|
* joining was called join_nodes(). It has been excised, living now only
|
2001-05-31 11:03:33 +00:00
|
|
|
* in the CVS history, but comments have been left behind that point to it just
|
join_nodes is removed; node joining is no longer done at any time, because
it corrupts active chains and can change the data at a node which
someone might be using. (In the latter case, though the full name from
the root was still intact, and the ->data member was not altered, other
values like the partial name ndata and offsets could change.)
New functions dns_rbt_fullnamefromnode() and dns_rbt_formatnodename(),
for getting a dns_name_t and filling a char buffer, respectively, with
the complete name of a node from the root.
rbtnode.is_root was being treated as isc_boolean_t even though it was bitfield
of width 1. This worked fine, but wasn't consistent with ISC style, and
would have puked if someone decided to change isc_true from 1 (unlikely
as that is). Yeah, anal, I know.
2001-03-08 01:08:39 +00:00
|
|
|
* in case someone wants to muck with this some more.
|
|
|
|
*
|
|
|
|
* The one positive aspect of all of this is that joining used to have a
|
|
|
|
* case where it might fail. Without trying to join, now this function always
|
2001-11-09 01:53:20 +00:00
|
|
|
* succeeds. It still returns isc_result_t, though, so the API wouldn't change.
|
|
|
|
*/
|
2000-04-12 21:31:02 +00:00
|
|
|
isc_result_t
|
|
|
|
dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, isc_boolean_t recurse)
|
|
|
|
{
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnode_t *parent;
|
|
|
|
|
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
REQUIRE(DNS_RBTNODE_VALID(node));
|
2012-06-20 14:13:12 -05:00
|
|
|
INSIST(rbt->nodecount != 0);
|
2008-01-22 23:28:04 +00:00
|
|
|
|
|
|
|
if (DOWN(node) != NULL) {
|
|
|
|
if (recurse)
|
2012-06-20 14:13:12 -05:00
|
|
|
RUNTIME_CHECK(deletetree(rbt, DOWN(node))
|
2008-01-22 23:28:04 +00:00
|
|
|
== ISC_R_SUCCESS);
|
|
|
|
else {
|
|
|
|
if (DATA(node) != NULL && rbt->data_deleter != NULL)
|
|
|
|
rbt->data_deleter(DATA(node), rbt->deleter_arg);
|
|
|
|
DATA(node) = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since there is at least one node below this one and
|
|
|
|
* no recursion was requested, the deletion is
|
|
|
|
* complete. The down node from this node might be all
|
|
|
|
* by itself on a single level, so join_nodes() could
|
|
|
|
* be used to collapse the tree (with all the caveats
|
|
|
|
* of the comment at the start of this function).
|
|
|
|
*/
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note the node that points to the level of the node that is being
|
|
|
|
* deleted. If the deleted node is the top level, parent will be set
|
|
|
|
* to NULL.
|
|
|
|
*/
|
|
|
|
parent = find_up(node);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This node now has no down pointer (either because it didn't
|
|
|
|
* have one to start, or because it was recursively removed).
|
|
|
|
* So now the node needs to be removed from this level.
|
|
|
|
*/
|
2012-06-20 14:13:12 -05:00
|
|
|
deletefromlevel(node, parent == NULL ? &rbt->root : &DOWN(parent));
|
2008-01-22 23:28:04 +00:00
|
|
|
|
|
|
|
if (DATA(node) != NULL && rbt->data_deleter != NULL)
|
|
|
|
rbt->data_deleter(DATA(node), rbt->deleter_arg);
|
|
|
|
|
|
|
|
unhash_node(rbt, node);
|
2001-11-10 01:37:44 +00:00
|
|
|
#if DNS_RBT_USEMAGIC
|
2008-01-22 23:28:04 +00:00
|
|
|
node->magic = 0;
|
2001-11-10 01:37:44 +00:00
|
|
|
#endif
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnode_refdestroy(node);
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
freenode(rbt, &node);
|
2008-01-22 23:28:04 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* There are now two special cases that can exist that would
|
|
|
|
* not have existed if the tree had been created using only
|
|
|
|
* the names that now exist in it. (This is all related to
|
|
|
|
* join_nodes() as described in this function's introductory comment.)
|
|
|
|
* Both cases exist when the deleted node's parent (the node
|
|
|
|
* that pointed to the deleted node's level) is not null but
|
|
|
|
* it has no data: parent != NULL && DATA(parent) == NULL.
|
|
|
|
*
|
|
|
|
* The first case is that the deleted node was the last on its level:
|
|
|
|
* DOWN(parent) == NULL. This case can only exist if the parent was
|
|
|
|
* previously deleted -- and so now, apparently, the parent should go
|
|
|
|
* away. That can't be done though because there might be external
|
|
|
|
* references to it, such as through a nodechain.
|
|
|
|
*
|
|
|
|
* The other case also involves a parent with no data, but with the
|
|
|
|
* deleted node being the next-to-last node instead of the last:
|
|
|
|
* LEFT(DOWN(parent)) == NULL && RIGHT(DOWN(parent)) == NULL.
|
|
|
|
* Presumably now the remaining node on the level should be joined
|
|
|
|
* with the parent, but it's already been described why that can't be
|
|
|
|
* done.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function never fails.
|
|
|
|
*/
|
|
|
|
return (ISC_R_SUCCESS);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_namefromnode(dns_rbtnode_t *node, dns_name_t *name) {
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
REQUIRE(DNS_RBTNODE_VALID(node));
|
|
|
|
REQUIRE(name != NULL);
|
|
|
|
REQUIRE(name->offsets == NULL);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
NODENAME(node, name);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
join_nodes is removed; node joining is no longer done at any time, because
it corrupts active chains and can change the data at a node which
someone might be using. (In the latter case, though the full name from
the root was still intact, and the ->data member was not altered, other
values like the partial name ndata and offsets could change.)
New functions dns_rbt_fullnamefromnode() and dns_rbt_formatnodename(),
for getting a dns_name_t and filling a char buffer, respectively, with
the complete name of a node from the root.
rbtnode.is_root was being treated as isc_boolean_t even though it was bitfield
of width 1. This worked fine, but wasn't consistent with ISC style, and
would have puked if someone decided to change isc_true from 1 (unlikely
as that is). Yeah, anal, I know.
2001-03-08 01:08:39 +00:00
|
|
|
isc_result_t
|
|
|
|
dns_rbt_fullnamefromnode(dns_rbtnode_t *node, dns_name_t *name) {
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_name_t current;
|
|
|
|
isc_result_t result;
|
join_nodes is removed; node joining is no longer done at any time, because
it corrupts active chains and can change the data at a node which
someone might be using. (In the latter case, though the full name from
the root was still intact, and the ->data member was not altered, other
values like the partial name ndata and offsets could change.)
New functions dns_rbt_fullnamefromnode() and dns_rbt_formatnodename(),
for getting a dns_name_t and filling a char buffer, respectively, with
the complete name of a node from the root.
rbtnode.is_root was being treated as isc_boolean_t even though it was bitfield
of width 1. This worked fine, but wasn't consistent with ISC style, and
would have puked if someone decided to change isc_true from 1 (unlikely
as that is). Yeah, anal, I know.
2001-03-08 01:08:39 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
REQUIRE(DNS_RBTNODE_VALID(node));
|
|
|
|
REQUIRE(name != NULL);
|
|
|
|
REQUIRE(name->buffer != NULL);
|
join_nodes is removed; node joining is no longer done at any time, because
it corrupts active chains and can change the data at a node which
someone might be using. (In the latter case, though the full name from
the root was still intact, and the ->data member was not altered, other
values like the partial name ndata and offsets could change.)
New functions dns_rbt_fullnamefromnode() and dns_rbt_formatnodename(),
for getting a dns_name_t and filling a char buffer, respectively, with
the complete name of a node from the root.
rbtnode.is_root was being treated as isc_boolean_t even though it was bitfield
of width 1. This worked fine, but wasn't consistent with ISC style, and
would have puked if someone decided to change isc_true from 1 (unlikely
as that is). Yeah, anal, I know.
2001-03-08 01:08:39 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_name_init(¤t, NULL);
|
|
|
|
dns_name_reset(name);
|
join_nodes is removed; node joining is no longer done at any time, because
it corrupts active chains and can change the data at a node which
someone might be using. (In the latter case, though the full name from
the root was still intact, and the ->data member was not altered, other
values like the partial name ndata and offsets could change.)
New functions dns_rbt_fullnamefromnode() and dns_rbt_formatnodename(),
for getting a dns_name_t and filling a char buffer, respectively, with
the complete name of a node from the root.
rbtnode.is_root was being treated as isc_boolean_t even though it was bitfield
of width 1. This worked fine, but wasn't consistent with ISC style, and
would have puked if someone decided to change isc_true from 1 (unlikely
as that is). Yeah, anal, I know.
2001-03-08 01:08:39 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
do {
|
|
|
|
INSIST(node != NULL);
|
join_nodes is removed; node joining is no longer done at any time, because
it corrupts active chains and can change the data at a node which
someone might be using. (In the latter case, though the full name from
the root was still intact, and the ->data member was not altered, other
values like the partial name ndata and offsets could change.)
New functions dns_rbt_fullnamefromnode() and dns_rbt_formatnodename(),
for getting a dns_name_t and filling a char buffer, respectively, with
the complete name of a node from the root.
rbtnode.is_root was being treated as isc_boolean_t even though it was bitfield
of width 1. This worked fine, but wasn't consistent with ISC style, and
would have puked if someone decided to change isc_true from 1 (unlikely
as that is). Yeah, anal, I know.
2001-03-08 01:08:39 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
NODENAME(node, ¤t);
|
join_nodes is removed; node joining is no longer done at any time, because
it corrupts active chains and can change the data at a node which
someone might be using. (In the latter case, though the full name from
the root was still intact, and the ->data member was not altered, other
values like the partial name ndata and offsets could change.)
New functions dns_rbt_fullnamefromnode() and dns_rbt_formatnodename(),
for getting a dns_name_t and filling a char buffer, respectively, with
the complete name of a node from the root.
rbtnode.is_root was being treated as isc_boolean_t even though it was bitfield
of width 1. This worked fine, but wasn't consistent with ISC style, and
would have puked if someone decided to change isc_true from 1 (unlikely
as that is). Yeah, anal, I know.
2001-03-08 01:08:39 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
result = dns_name_concatenate(name, ¤t, name, NULL);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
break;
|
join_nodes is removed; node joining is no longer done at any time, because
it corrupts active chains and can change the data at a node which
someone might be using. (In the latter case, though the full name from
the root was still intact, and the ->data member was not altered, other
values like the partial name ndata and offsets could change.)
New functions dns_rbt_fullnamefromnode() and dns_rbt_formatnodename(),
for getting a dns_name_t and filling a char buffer, respectively, with
the complete name of a node from the root.
rbtnode.is_root was being treated as isc_boolean_t even though it was bitfield
of width 1. This worked fine, but wasn't consistent with ISC style, and
would have puked if someone decided to change isc_true from 1 (unlikely
as that is). Yeah, anal, I know.
2001-03-08 01:08:39 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
node = find_up(node);
|
|
|
|
} while (! dns_name_isabsolute(name));
|
2007-10-19 17:15:53 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
return (result);
|
join_nodes is removed; node joining is no longer done at any time, because
it corrupts active chains and can change the data at a node which
someone might be using. (In the latter case, though the full name from
the root was still intact, and the ->data member was not altered, other
values like the partial name ndata and offsets could change.)
New functions dns_rbt_fullnamefromnode() and dns_rbt_formatnodename(),
for getting a dns_name_t and filling a char buffer, respectively, with
the complete name of a node from the root.
rbtnode.is_root was being treated as isc_boolean_t even though it was bitfield
of width 1. This worked fine, but wasn't consistent with ISC style, and
would have puked if someone decided to change isc_true from 1 (unlikely
as that is). Yeah, anal, I know.
2001-03-08 01:08:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
dns_rbt_formatnodename(dns_rbtnode_t *node, char *printname, unsigned int size)
|
|
|
|
{
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_fixedname_t fixedname;
|
|
|
|
dns_name_t *name;
|
|
|
|
isc_result_t result;
|
|
|
|
|
|
|
|
REQUIRE(DNS_RBTNODE_VALID(node));
|
|
|
|
REQUIRE(printname != NULL);
|
|
|
|
|
|
|
|
dns_fixedname_init(&fixedname);
|
|
|
|
name = dns_fixedname_name(&fixedname);
|
|
|
|
result = dns_rbt_fullnamefromnode(node, name);
|
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
dns_name_format(name, printname, size);
|
|
|
|
else
|
|
|
|
snprintf(printname, size, "<error building name: %s>",
|
|
|
|
dns_result_totext(result));
|
|
|
|
|
|
|
|
return (printname);
|
join_nodes is removed; node joining is no longer done at any time, because
it corrupts active chains and can change the data at a node which
someone might be using. (In the latter case, though the full name from
the root was still intact, and the ->data member was not altered, other
values like the partial name ndata and offsets could change.)
New functions dns_rbt_fullnamefromnode() and dns_rbt_formatnodename(),
for getting a dns_name_t and filling a char buffer, respectively, with
the complete name of a node from the root.
rbtnode.is_root was being treated as isc_boolean_t even though it was bitfield
of width 1. This worked fine, but wasn't consistent with ISC style, and
would have puked if someone decided to change isc_true from 1 (unlikely
as that is). Yeah, anal, I know.
2001-03-08 01:08:39 +00:00
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
static isc_result_t
|
1999-01-25 15:46:30 +00:00
|
|
|
create_node(isc_mem_t *mctx, dns_name_t *name, dns_rbtnode_t **nodep) {
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnode_t *node;
|
|
|
|
isc_region_t region;
|
|
|
|
unsigned int labels;
|
2012-06-20 14:13:12 -05:00
|
|
|
size_t nodelen;
|
2008-01-22 23:28:04 +00:00
|
|
|
|
|
|
|
REQUIRE(name->offsets != NULL);
|
|
|
|
|
|
|
|
dns_name_toregion(name, ®ion);
|
|
|
|
labels = dns_name_countlabels(name);
|
|
|
|
ENSURE(labels > 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate space for the node structure, the name, and the offsets.
|
|
|
|
*/
|
2012-06-20 14:13:12 -05:00
|
|
|
nodelen = sizeof(dns_rbtnode_t) + region.length + labels + 1;
|
|
|
|
node = (dns_rbtnode_t *)isc_mem_get(mctx, nodelen);
|
2008-01-22 23:28:04 +00:00
|
|
|
if (node == NULL)
|
|
|
|
return (ISC_R_NOMEMORY);
|
2012-06-20 14:13:12 -05:00
|
|
|
memset(node, 0, nodelen);
|
2008-01-22 23:28:04 +00:00
|
|
|
|
|
|
|
node->is_root = 0;
|
|
|
|
PARENT(node) = NULL;
|
|
|
|
RIGHT(node) = NULL;
|
|
|
|
LEFT(node) = NULL;
|
|
|
|
DOWN(node) = NULL;
|
|
|
|
DATA(node) = NULL;
|
2012-06-20 14:13:12 -05:00
|
|
|
node->is_mmapped = 0;
|
|
|
|
node->down_is_relative = 0;
|
|
|
|
node->left_is_relative = 0;
|
|
|
|
node->right_is_relative = 0;
|
|
|
|
node->parent_is_relative = 0;
|
|
|
|
node->data_is_relative = 0;
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2000-11-18 00:55:25 +00:00
|
|
|
#ifdef DNS_RBT_USEHASH
|
2008-01-22 23:28:04 +00:00
|
|
|
HASHNEXT(node) = NULL;
|
|
|
|
HASHVAL(node) = 0;
|
2000-11-18 00:55:25 +00:00
|
|
|
#endif
|
1999-01-31 00:52:53 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
ISC_LINK_INIT(node, deadlink);
|
|
|
|
|
|
|
|
LOCKNUM(node) = 0;
|
|
|
|
WILD(node) = 0;
|
|
|
|
DIRTY(node) = 0;
|
|
|
|
dns_rbtnode_refinit(node, 0);
|
|
|
|
node->find_callback = 0;
|
2009-10-27 04:46:58 +00:00
|
|
|
node->nsec = DNS_RBT_NSEC_NORMAL;
|
2008-01-22 23:28:04 +00:00
|
|
|
|
|
|
|
MAKE_BLACK(node);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The following is stored to make reconstructing a name from the
|
|
|
|
* stored value in the node easy: the length of the name, the number
|
|
|
|
* of labels, whether the name is absolute or not, the name itself,
|
|
|
|
* and the name's offsets table.
|
|
|
|
*
|
|
|
|
* XXX RTH
|
|
|
|
* The offsets table could be made smaller by eliminating the
|
|
|
|
* first offset, which is always 0. This requires changes to
|
|
|
|
* lib/dns/name.c.
|
2009-10-20 04:57:57 +00:00
|
|
|
*
|
|
|
|
* Note: OLDOFFSETLEN *must* be assigned *after* OLDNAMELEN is assigned
|
|
|
|
* as it uses OLDNAMELEN.
|
2008-01-22 23:28:04 +00:00
|
|
|
*/
|
2009-10-20 04:57:57 +00:00
|
|
|
OLDNAMELEN(node) = NAMELEN(node) = region.length;
|
|
|
|
OLDOFFSETLEN(node) = OFFSETLEN(node) = labels;
|
2008-01-22 23:28:04 +00:00
|
|
|
ATTRS(node) = name->attributes;
|
|
|
|
|
|
|
|
memcpy(NAME(node), region.base, region.length);
|
|
|
|
memcpy(OFFSETS(node), name->offsets, labels);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2001-11-10 01:37:44 +00:00
|
|
|
#if DNS_RBT_USEMAGIC
|
2008-01-22 23:28:04 +00:00
|
|
|
node->magic = DNS_RBTNODE_MAGIC;
|
2001-11-10 01:37:44 +00:00
|
|
|
#endif
|
2008-01-22 23:28:04 +00:00
|
|
|
*nodep = node;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
2000-11-18 00:55:25 +00:00
|
|
|
#ifdef DNS_RBT_USEHASH
|
2000-10-25 07:21:31 +00:00
|
|
|
static inline void
|
2004-10-25 01:27:54 +00:00
|
|
|
hash_add_node(dns_rbt_t *rbt, dns_rbtnode_t *node, dns_name_t *name) {
|
2008-01-22 23:28:04 +00:00
|
|
|
unsigned int hash;
|
2000-10-25 07:21:31 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
REQUIRE(name != NULL);
|
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
HASHVAL(node) = dns_name_fullhash(name, ISC_FALSE);
|
2000-10-25 07:21:31 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
hash = HASHVAL(node) % rbt->hashsize;
|
|
|
|
HASHNEXT(node) = rbt->hashtable[hash];
|
2000-10-25 07:21:31 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
rbt->hashtable[hash] = node;
|
2000-10-25 07:21:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static isc_result_t
|
|
|
|
inithash(dns_rbt_t *rbt) {
|
2008-01-22 23:28:04 +00:00
|
|
|
unsigned int bytes;
|
2000-10-25 07:21:31 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
rbt->hashsize = RBT_HASH_SIZE;
|
|
|
|
bytes = rbt->hashsize * sizeof(dns_rbtnode_t *);
|
|
|
|
rbt->hashtable = isc_mem_get(rbt->mctx, bytes);
|
2000-10-25 07:21:31 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
if (rbt->hashtable == NULL)
|
|
|
|
return (ISC_R_NOMEMORY);
|
2000-10-25 07:21:31 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
memset(rbt->hashtable, 0, bytes);
|
2000-10-25 07:21:31 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
2000-10-25 07:21:31 +00:00
|
|
|
}
|
|
|
|
|
2004-03-03 22:40:56 +00:00
|
|
|
static void
|
2000-10-25 07:21:31 +00:00
|
|
|
rehash(dns_rbt_t *rbt) {
|
2008-01-22 23:28:04 +00:00
|
|
|
unsigned int oldsize;
|
|
|
|
dns_rbtnode_t **oldtable;
|
|
|
|
dns_rbtnode_t *node;
|
|
|
|
unsigned int hash;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
oldsize = rbt->hashsize;
|
|
|
|
oldtable = rbt->hashtable;
|
2011-03-11 06:11:27 +00:00
|
|
|
rbt->hashsize = rbt->hashsize * 2 + 1;
|
2008-01-22 23:28:04 +00:00
|
|
|
rbt->hashtable = isc_mem_get(rbt->mctx,
|
|
|
|
rbt->hashsize * sizeof(dns_rbtnode_t *));
|
|
|
|
if (rbt->hashtable == NULL) {
|
|
|
|
rbt->hashtable = oldtable;
|
|
|
|
rbt->hashsize = oldsize;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-30 18:50:38 +11:00
|
|
|
INSIST(rbt->hashsize > 0);
|
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
for (i = 0; i < rbt->hashsize; i++)
|
|
|
|
rbt->hashtable[i] = NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < oldsize; i++) {
|
|
|
|
node = oldtable[i];
|
|
|
|
while (node != NULL) {
|
|
|
|
hash = HASHVAL(node) % rbt->hashsize;
|
|
|
|
oldtable[i] = HASHNEXT(node);
|
|
|
|
HASHNEXT(node) = rbt->hashtable[hash];
|
|
|
|
rbt->hashtable[hash] = node;
|
|
|
|
node = oldtable[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isc_mem_put(rbt->mctx, oldtable, oldsize * sizeof(dns_rbtnode_t *));
|
2000-10-25 07:21:31 +00:00
|
|
|
}
|
|
|
|
|
2004-03-03 22:40:56 +00:00
|
|
|
static inline void
|
2004-10-25 01:27:54 +00:00
|
|
|
hash_node(dns_rbt_t *rbt, dns_rbtnode_t *node, dns_name_t *name) {
|
2000-10-25 07:21:31 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
REQUIRE(DNS_RBTNODE_VALID(node));
|
2001-11-10 01:37:44 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
if (rbt->nodecount >= (rbt->hashsize *3))
|
|
|
|
rehash(rbt);
|
2000-10-25 07:21:31 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
hash_add_node(rbt, node, name);
|
2000-10-25 07:21:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
unhash_node(dns_rbt_t *rbt, dns_rbtnode_t *node) {
|
2008-01-22 23:28:04 +00:00
|
|
|
unsigned int bucket;
|
|
|
|
dns_rbtnode_t *bucket_node;
|
|
|
|
|
|
|
|
REQUIRE(DNS_RBTNODE_VALID(node));
|
|
|
|
|
|
|
|
if (rbt->hashtable != NULL) {
|
|
|
|
bucket = HASHVAL(node) % rbt->hashsize;
|
|
|
|
bucket_node = rbt->hashtable[bucket];
|
|
|
|
|
|
|
|
if (bucket_node == node)
|
|
|
|
rbt->hashtable[bucket] = HASHNEXT(node);
|
|
|
|
else {
|
|
|
|
while (HASHNEXT(bucket_node) != node) {
|
|
|
|
INSIST(HASHNEXT(bucket_node) != NULL);
|
|
|
|
bucket_node = HASHNEXT(bucket_node);
|
|
|
|
}
|
|
|
|
HASHNEXT(bucket_node) = HASHNEXT(node);
|
|
|
|
}
|
|
|
|
}
|
2000-10-25 07:21:31 +00:00
|
|
|
}
|
2000-11-18 00:55:25 +00:00
|
|
|
#endif /* DNS_RBT_USEHASH */
|
2000-10-25 07:21:31 +00:00
|
|
|
|
1999-01-20 10:13:43 +00:00
|
|
|
static inline void
|
2000-05-19 04:42:08 +00:00
|
|
|
rotate_left(dns_rbtnode_t *node, dns_rbtnode_t **rootp) {
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnode_t *child;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
REQUIRE(DNS_RBTNODE_VALID(node));
|
|
|
|
REQUIRE(rootp != NULL);
|
2000-04-06 18:47:07 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
child = RIGHT(node);
|
|
|
|
INSIST(child != NULL);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
RIGHT(node) = LEFT(child);
|
|
|
|
if (LEFT(child) != NULL)
|
|
|
|
PARENT(LEFT(child)) = node;
|
|
|
|
LEFT(child) = node;
|
1999-01-25 15:46:30 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
if (child != NULL)
|
|
|
|
PARENT(child) = PARENT(node);
|
2000-04-06 18:47:07 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
if (IS_ROOT(node)) {
|
|
|
|
*rootp = child;
|
|
|
|
child->is_root = 1;
|
|
|
|
node->is_root = 0;
|
2000-04-12 21:31:02 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
} else {
|
|
|
|
if (LEFT(PARENT(node)) == node)
|
|
|
|
LEFT(PARENT(node)) = child;
|
|
|
|
else
|
|
|
|
RIGHT(PARENT(node)) = child;
|
|
|
|
}
|
2000-04-06 18:47:07 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
PARENT(node) = child;
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
2000-05-19 04:42:08 +00:00
|
|
|
rotate_right(dns_rbtnode_t *node, dns_rbtnode_t **rootp) {
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnode_t *child;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
REQUIRE(DNS_RBTNODE_VALID(node));
|
|
|
|
REQUIRE(rootp != NULL);
|
2000-04-06 18:47:07 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
child = LEFT(node);
|
|
|
|
INSIST(child != NULL);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
LEFT(node) = RIGHT(child);
|
|
|
|
if (RIGHT(child) != NULL)
|
|
|
|
PARENT(RIGHT(child)) = node;
|
|
|
|
RIGHT(child) = node;
|
1999-01-25 15:46:30 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
if (child != NULL)
|
|
|
|
PARENT(child) = PARENT(node);
|
2000-04-06 18:47:07 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
if (IS_ROOT(node)) {
|
|
|
|
*rootp = child;
|
|
|
|
child->is_root = 1;
|
|
|
|
node->is_root = 0;
|
2000-04-12 21:31:02 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
} else {
|
|
|
|
if (LEFT(PARENT(node)) == node)
|
|
|
|
LEFT(PARENT(node)) = child;
|
|
|
|
else
|
|
|
|
RIGHT(PARENT(node)) = child;
|
|
|
|
}
|
2000-04-06 18:47:07 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
PARENT(node) = child;
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-01-27 01:48:55 +00:00
|
|
|
/*
|
|
|
|
* This is the real workhorse of the insertion code, because it does the
|
|
|
|
* true red/black tree on a single level.
|
|
|
|
*/
|
1999-03-12 05:00:32 +00:00
|
|
|
static void
|
2012-06-20 14:13:12 -05:00
|
|
|
addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order,
|
|
|
|
dns_rbtnode_t **rootp)
|
1999-03-03 20:01:49 +00:00
|
|
|
{
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnode_t *child, *root, *parent, *grandparent;
|
|
|
|
dns_name_t add_name, current_name;
|
|
|
|
dns_offsets_t add_offsets, current_offsets;
|
|
|
|
|
|
|
|
REQUIRE(rootp != NULL);
|
|
|
|
REQUIRE(DNS_RBTNODE_VALID(node) && LEFT(node) == NULL &&
|
|
|
|
RIGHT(node) == NULL);
|
|
|
|
REQUIRE(current != NULL);
|
|
|
|
|
|
|
|
root = *rootp;
|
|
|
|
if (root == NULL) {
|
|
|
|
/*
|
|
|
|
* First node of a level.
|
|
|
|
*/
|
|
|
|
MAKE_BLACK(node);
|
|
|
|
node->is_root = 1;
|
|
|
|
PARENT(node) = current;
|
|
|
|
*rootp = node;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
child = root;
|
2011-03-11 06:11:27 +00:00
|
|
|
POST(child);
|
2008-01-22 23:28:04 +00:00
|
|
|
|
|
|
|
dns_name_init(&add_name, add_offsets);
|
|
|
|
NODENAME(node, &add_name);
|
|
|
|
|
|
|
|
dns_name_init(¤t_name, current_offsets);
|
|
|
|
NODENAME(current, ¤t_name);
|
|
|
|
|
|
|
|
if (order < 0) {
|
|
|
|
INSIST(LEFT(current) == NULL);
|
|
|
|
LEFT(current) = node;
|
|
|
|
} else {
|
|
|
|
INSIST(RIGHT(current) == NULL);
|
|
|
|
RIGHT(current) = node;
|
|
|
|
}
|
|
|
|
|
|
|
|
INSIST(PARENT(node) == NULL);
|
|
|
|
PARENT(node) = current;
|
|
|
|
|
|
|
|
MAKE_RED(node);
|
|
|
|
|
|
|
|
while (node != root && IS_RED(PARENT(node))) {
|
|
|
|
/*
|
|
|
|
* XXXDCL could do away with separate parent and grandparent
|
|
|
|
* variables. They are vestiges of the days before parent
|
|
|
|
* pointers. However, they make the code a little clearer.
|
|
|
|
*/
|
|
|
|
|
|
|
|
parent = PARENT(node);
|
|
|
|
grandparent = PARENT(parent);
|
|
|
|
|
|
|
|
if (parent == LEFT(grandparent)) {
|
|
|
|
child = RIGHT(grandparent);
|
|
|
|
if (child != NULL && IS_RED(child)) {
|
|
|
|
MAKE_BLACK(parent);
|
|
|
|
MAKE_BLACK(child);
|
|
|
|
MAKE_RED(grandparent);
|
|
|
|
node = grandparent;
|
|
|
|
} else {
|
|
|
|
if (node == RIGHT(parent)) {
|
|
|
|
rotate_left(parent, &root);
|
|
|
|
node = parent;
|
|
|
|
parent = PARENT(node);
|
|
|
|
grandparent = PARENT(parent);
|
|
|
|
}
|
|
|
|
MAKE_BLACK(parent);
|
|
|
|
MAKE_RED(grandparent);
|
|
|
|
rotate_right(grandparent, &root);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
child = LEFT(grandparent);
|
|
|
|
if (child != NULL && IS_RED(child)) {
|
|
|
|
MAKE_BLACK(parent);
|
|
|
|
MAKE_BLACK(child);
|
|
|
|
MAKE_RED(grandparent);
|
|
|
|
node = grandparent;
|
|
|
|
} else {
|
|
|
|
if (node == LEFT(parent)) {
|
|
|
|
rotate_right(parent, &root);
|
|
|
|
node = parent;
|
|
|
|
parent = PARENT(node);
|
|
|
|
grandparent = PARENT(parent);
|
|
|
|
}
|
|
|
|
MAKE_BLACK(parent);
|
|
|
|
MAKE_RED(grandparent);
|
|
|
|
rotate_left(grandparent, &root);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MAKE_BLACK(root);
|
|
|
|
ENSURE(IS_ROOT(root));
|
|
|
|
*rootp = root;
|
|
|
|
|
|
|
|
return;
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-01-31 00:52:53 +00:00
|
|
|
/*
|
|
|
|
* This is the real workhorse of the deletion code, because it does the
|
|
|
|
* true red/black tree on a single level.
|
|
|
|
*/
|
|
|
|
static void
|
2012-06-20 14:13:12 -05:00
|
|
|
deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp) {
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnode_t *child, *sibling, *parent;
|
|
|
|
dns_rbtnode_t *successor;
|
|
|
|
|
|
|
|
REQUIRE(delete != NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Verify that the parent history is (apparently) correct.
|
|
|
|
*/
|
|
|
|
INSIST((IS_ROOT(delete) && *rootp == delete) ||
|
|
|
|
(! IS_ROOT(delete) &&
|
|
|
|
(LEFT(PARENT(delete)) == delete ||
|
|
|
|
RIGHT(PARENT(delete)) == delete)));
|
|
|
|
|
|
|
|
child = NULL;
|
|
|
|
|
|
|
|
if (LEFT(delete) == NULL) {
|
|
|
|
if (RIGHT(delete) == NULL) {
|
|
|
|
if (IS_ROOT(delete)) {
|
|
|
|
/*
|
|
|
|
* This is the only item in the tree.
|
|
|
|
*/
|
|
|
|
*rootp = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
/*
|
|
|
|
* This node has one child, on the right.
|
|
|
|
*/
|
|
|
|
child = RIGHT(delete);
|
|
|
|
|
|
|
|
} else if (RIGHT(delete) == NULL)
|
|
|
|
/*
|
|
|
|
* This node has one child, on the left.
|
|
|
|
*/
|
|
|
|
child = LEFT(delete);
|
|
|
|
else {
|
|
|
|
dns_rbtnode_t holder, *tmp = &holder;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This node has two children, so it cannot be directly
|
|
|
|
* deleted. Find its immediate in-order successor and
|
|
|
|
* move it to this location, then do the deletion at the
|
|
|
|
* old site of the successor.
|
|
|
|
*/
|
|
|
|
successor = RIGHT(delete);
|
|
|
|
while (LEFT(successor) != NULL)
|
|
|
|
successor = LEFT(successor);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The successor cannot possibly have a left child;
|
|
|
|
* if there is any child, it is on the right.
|
|
|
|
*/
|
|
|
|
if (RIGHT(successor) != NULL)
|
|
|
|
child = RIGHT(successor);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Swap the two nodes; it would be simpler to just replace
|
|
|
|
* the value being deleted with that of the successor,
|
|
|
|
* but this rigamarole is done so the caller has complete
|
|
|
|
* control over the pointers (and memory allocation) of
|
|
|
|
* all of nodes. If just the key value were removed from
|
|
|
|
* the tree, the pointer to the node would be unchanged.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First, put the successor in the tree location of the
|
|
|
|
* node to be deleted. Save its existing tree pointer
|
|
|
|
* information, which will be needed when linking up
|
|
|
|
* delete to the successor's old location.
|
|
|
|
*/
|
|
|
|
memcpy(tmp, successor, sizeof(dns_rbtnode_t));
|
|
|
|
|
|
|
|
if (IS_ROOT(delete)) {
|
|
|
|
*rootp = successor;
|
|
|
|
successor->is_root = ISC_TRUE;
|
|
|
|
delete->is_root = ISC_FALSE;
|
|
|
|
|
|
|
|
} else
|
|
|
|
if (LEFT(PARENT(delete)) == delete)
|
|
|
|
LEFT(PARENT(delete)) = successor;
|
|
|
|
else
|
|
|
|
RIGHT(PARENT(delete)) = successor;
|
|
|
|
|
|
|
|
PARENT(successor) = PARENT(delete);
|
|
|
|
LEFT(successor) = LEFT(delete);
|
|
|
|
RIGHT(successor) = RIGHT(delete);
|
|
|
|
COLOR(successor) = COLOR(delete);
|
|
|
|
|
|
|
|
if (LEFT(successor) != NULL)
|
|
|
|
PARENT(LEFT(successor)) = successor;
|
|
|
|
if (RIGHT(successor) != successor)
|
|
|
|
PARENT(RIGHT(successor)) = successor;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now relink the node to be deleted into the
|
|
|
|
* successor's previous tree location. PARENT(tmp)
|
|
|
|
* is the successor's original parent.
|
|
|
|
*/
|
|
|
|
INSIST(! IS_ROOT(delete));
|
|
|
|
|
|
|
|
if (PARENT(tmp) == delete) {
|
|
|
|
/*
|
|
|
|
* Node being deleted was successor's parent.
|
|
|
|
*/
|
|
|
|
RIGHT(successor) = delete;
|
|
|
|
PARENT(delete) = successor;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
LEFT(PARENT(tmp)) = delete;
|
|
|
|
PARENT(delete) = PARENT(tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Original location of successor node has no left.
|
|
|
|
*/
|
|
|
|
LEFT(delete) = NULL;
|
|
|
|
RIGHT(delete) = RIGHT(tmp);
|
|
|
|
COLOR(delete) = COLOR(tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove the node by removing the links from its parent.
|
|
|
|
*/
|
|
|
|
if (! IS_ROOT(delete)) {
|
|
|
|
if (LEFT(PARENT(delete)) == delete)
|
|
|
|
LEFT(PARENT(delete)) = child;
|
|
|
|
else
|
|
|
|
RIGHT(PARENT(delete)) = child;
|
|
|
|
|
|
|
|
if (child != NULL)
|
|
|
|
PARENT(child) = PARENT(delete);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* This is the root being deleted, and at this point
|
|
|
|
* it is known to have just one child.
|
|
|
|
*/
|
|
|
|
*rootp = child;
|
|
|
|
child->is_root = 1;
|
|
|
|
PARENT(child) = PARENT(delete);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fix color violations.
|
|
|
|
*/
|
|
|
|
if (IS_BLACK(delete)) {
|
|
|
|
parent = PARENT(delete);
|
|
|
|
|
|
|
|
while (child != *rootp && IS_BLACK(child)) {
|
|
|
|
INSIST(child == NULL || ! IS_ROOT(child));
|
|
|
|
|
|
|
|
if (LEFT(parent) == child) {
|
|
|
|
sibling = RIGHT(parent);
|
|
|
|
|
|
|
|
if (IS_RED(sibling)) {
|
|
|
|
MAKE_BLACK(sibling);
|
|
|
|
MAKE_RED(parent);
|
|
|
|
rotate_left(parent, rootp);
|
|
|
|
sibling = RIGHT(parent);
|
|
|
|
}
|
|
|
|
|
2011-08-25 05:56:50 +00:00
|
|
|
INSIST(sibling != NULL);
|
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
if (IS_BLACK(LEFT(sibling)) &&
|
|
|
|
IS_BLACK(RIGHT(sibling))) {
|
|
|
|
MAKE_RED(sibling);
|
|
|
|
child = parent;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if (IS_BLACK(RIGHT(sibling))) {
|
|
|
|
MAKE_BLACK(LEFT(sibling));
|
|
|
|
MAKE_RED(sibling);
|
|
|
|
rotate_right(sibling, rootp);
|
|
|
|
sibling = RIGHT(parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
COLOR(sibling) = COLOR(parent);
|
|
|
|
MAKE_BLACK(parent);
|
2012-11-30 18:50:38 +11:00
|
|
|
INSIST(RIGHT(sibling) != NULL);
|
2008-01-22 23:28:04 +00:00
|
|
|
MAKE_BLACK(RIGHT(sibling));
|
|
|
|
rotate_left(parent, rootp);
|
|
|
|
child = *rootp;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Child is parent's right child.
|
2009-01-17 14:45:17 +00:00
|
|
|
* Everything is done the same as above,
|
2008-01-22 23:28:04 +00:00
|
|
|
* except mirrored.
|
|
|
|
*/
|
|
|
|
sibling = LEFT(parent);
|
|
|
|
|
|
|
|
if (IS_RED(sibling)) {
|
|
|
|
MAKE_BLACK(sibling);
|
|
|
|
MAKE_RED(parent);
|
|
|
|
rotate_right(parent, rootp);
|
|
|
|
sibling = LEFT(parent);
|
|
|
|
}
|
|
|
|
|
2011-08-25 05:56:50 +00:00
|
|
|
INSIST(sibling != NULL);
|
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
if (IS_BLACK(LEFT(sibling)) &&
|
|
|
|
IS_BLACK(RIGHT(sibling))) {
|
|
|
|
MAKE_RED(sibling);
|
|
|
|
child = parent;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (IS_BLACK(LEFT(sibling))) {
|
|
|
|
MAKE_BLACK(RIGHT(sibling));
|
|
|
|
MAKE_RED(sibling);
|
|
|
|
rotate_left(sibling, rootp);
|
|
|
|
sibling = LEFT(parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
COLOR(sibling) = COLOR(parent);
|
|
|
|
MAKE_BLACK(parent);
|
2012-11-30 18:50:38 +11:00
|
|
|
INSIST(LEFT(sibling) != NULL);
|
2008-01-22 23:28:04 +00:00
|
|
|
MAKE_BLACK(LEFT(sibling));
|
|
|
|
rotate_right(parent, rootp);
|
|
|
|
child = *rootp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
parent = PARENT(child);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_RED(child))
|
|
|
|
MAKE_BLACK(child);
|
|
|
|
}
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-01-26 03:31:53 +00:00
|
|
|
/*
|
|
|
|
* This should only be used on the root of a tree, because no color fixup
|
|
|
|
* is done at all.
|
1999-01-31 01:35:04 +00:00
|
|
|
*
|
|
|
|
* NOTE: No root pointer maintenance is done, because the function is only
|
|
|
|
* used for two cases:
|
1999-03-30 01:56:01 +00:00
|
|
|
* + deleting everything DOWN from a node that is itself being deleted, and
|
1999-01-31 01:35:04 +00:00
|
|
|
* + deleting the entire tree of trees from dns_rbt_destroy.
|
|
|
|
* In each case, the root pointer is no longer relevant, so there
|
|
|
|
* is no need for a root parameter to this function.
|
|
|
|
*
|
|
|
|
* If the function is ever intended to be used to delete something where
|
|
|
|
* a pointer needs to be told that this tree no longer exists,
|
1999-02-06 01:27:35 +00:00
|
|
|
* this function would need to adjusted accordingly.
|
1999-01-26 03:31:53 +00:00
|
|
|
*/
|
2004-03-04 06:56:41 +00:00
|
|
|
static isc_result_t
|
2012-06-20 14:13:12 -05:00
|
|
|
deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node) {
|
2008-01-22 23:28:04 +00:00
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
|
|
|
|
if (node == NULL)
|
|
|
|
return (result);
|
|
|
|
|
|
|
|
if (LEFT(node) != NULL) {
|
2012-06-20 14:13:12 -05:00
|
|
|
result = deletetree(rbt, LEFT(node));
|
2008-01-22 23:28:04 +00:00
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
goto done;
|
|
|
|
LEFT(node) = NULL;
|
|
|
|
}
|
|
|
|
if (RIGHT(node) != NULL) {
|
2012-06-20 14:13:12 -05:00
|
|
|
result = deletetree(rbt, RIGHT(node));
|
2008-01-22 23:28:04 +00:00
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
goto done;
|
|
|
|
RIGHT(node) = NULL;
|
|
|
|
}
|
|
|
|
if (DOWN(node) != NULL) {
|
2012-06-20 14:13:12 -05:00
|
|
|
result = deletetree(rbt, DOWN(node));
|
2008-01-22 23:28:04 +00:00
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
goto done;
|
|
|
|
DOWN(node) = NULL;
|
|
|
|
}
|
2004-03-04 06:56:41 +00:00
|
|
|
done:
|
2008-01-22 23:28:04 +00:00
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (result);
|
2004-03-04 06:56:41 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
if (DATA(node) != NULL && rbt->data_deleter != NULL)
|
|
|
|
rbt->data_deleter(DATA(node), rbt->deleter_arg);
|
2004-03-04 06:56:41 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
unhash_node(rbt, node);
|
2004-03-04 06:56:41 +00:00
|
|
|
#if DNS_RBT_USEMAGIC
|
2008-01-22 23:28:04 +00:00
|
|
|
node->magic = 0;
|
2004-03-04 06:56:41 +00:00
|
|
|
#endif
|
2007-10-19 17:15:53 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
freenode(rbt, &node);
|
2008-01-22 23:28:04 +00:00
|
|
|
return (result);
|
2004-03-04 06:56:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-06-20 14:13:12 -05:00
|
|
|
freenode(dns_rbt_t *rbt, dns_rbtnode_t **nodep) {
|
|
|
|
dns_rbtnode_t *node = *nodep;
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
if (node->is_mmapped == 0) {
|
|
|
|
isc_mem_put(rbt->mctx, node, NODE_SIZE(node));
|
|
|
|
}
|
|
|
|
*nodep = NULL;
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
rbt->nodecount--;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, dns_rbtnode_t **nodep) {
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnode_t *parent;
|
|
|
|
dns_rbtnode_t *node = *nodep;
|
|
|
|
REQUIRE(VALID_RBT(rbt));
|
2004-03-04 06:56:41 +00:00
|
|
|
|
|
|
|
again:
|
2008-01-22 23:28:04 +00:00
|
|
|
if (node == NULL) {
|
|
|
|
*nodep = NULL;
|
|
|
|
return;
|
|
|
|
}
|
1999-01-26 03:31:53 +00:00
|
|
|
|
2004-03-04 06:56:41 +00:00
|
|
|
traverse:
|
2008-01-22 23:28:04 +00:00
|
|
|
if (LEFT(node) != NULL) {
|
|
|
|
node = LEFT(node);
|
|
|
|
goto traverse;
|
|
|
|
}
|
|
|
|
if (DOWN(node) != NULL) {
|
|
|
|
node = DOWN(node);
|
|
|
|
goto traverse;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DATA(node) != NULL && rbt->data_deleter != NULL)
|
|
|
|
rbt->data_deleter(DATA(node), rbt->deleter_arg);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note: we don't call unhash_node() here as we are destroying
|
|
|
|
* the complete rbt tree.
|
|
|
|
*/
|
2001-11-10 01:37:44 +00:00
|
|
|
#if DNS_RBT_USEMAGIC
|
2008-01-22 23:28:04 +00:00
|
|
|
node->magic = 0;
|
2001-11-10 01:37:44 +00:00
|
|
|
#endif
|
2008-01-22 23:28:04 +00:00
|
|
|
parent = PARENT(node);
|
2008-03-31 13:11:32 +00:00
|
|
|
if (RIGHT(node) != NULL)
|
|
|
|
PARENT(RIGHT(node)) = parent;
|
2008-01-22 23:28:04 +00:00
|
|
|
if (parent != NULL) {
|
|
|
|
if (LEFT(parent) == node)
|
2008-03-31 13:11:32 +00:00
|
|
|
LEFT(parent) = RIGHT(node);
|
2008-01-22 23:28:04 +00:00
|
|
|
else if (DOWN(parent) == node)
|
2008-03-31 13:11:32 +00:00
|
|
|
DOWN(parent) = RIGHT(node);
|
|
|
|
} else
|
|
|
|
parent = RIGHT(node);
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
freenode(rbt, &node);
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
node = parent;
|
|
|
|
if (quantum != 0 && --quantum == 0) {
|
|
|
|
*nodep = node;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
goto again;
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-03-12 05:00:32 +00:00
|
|
|
static void
|
1999-01-25 15:46:30 +00:00
|
|
|
dns_rbt_indent(int depth) {
|
2008-01-22 23:28:04 +00:00
|
|
|
int i;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
printf("%4d ", depth);
|
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
for (i = 0; i < depth; i++)
|
2012-06-20 14:13:12 -05:00
|
|
|
printf("- ");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dns_rbt_printnodeinfo(dns_rbtnode_t *n) {
|
|
|
|
printf("Node info for nodename: ");
|
|
|
|
printnodename(n);
|
|
|
|
printf("\n");
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
printf("n = %p\n", n);
|
|
|
|
|
2012-06-20 23:46:40 +00:00
|
|
|
printf("Relative pointers: %s%s%s%s%s\n",
|
2012-06-20 14:13:12 -05:00
|
|
|
(n->parent_is_relative == 1 ? " P" : ""),
|
|
|
|
(n->right_is_relative == 1 ? " R" : ""),
|
|
|
|
(n->left_is_relative == 1 ? " L" : ""),
|
|
|
|
(n->down_is_relative == 1 ? " D" : ""),
|
|
|
|
(n->data_is_relative == 1 ? " T" : ""));
|
|
|
|
|
|
|
|
printf("node lock address = %d\n", n->locknum);
|
|
|
|
|
|
|
|
printf("Parent: %p\n", n->parent);
|
|
|
|
printf("Right: %p\n", n->right);
|
|
|
|
printf("Left: %p\n", n->left);
|
|
|
|
printf("Down: %p\n", n->down);
|
|
|
|
printf("daTa: %p\n", n->data);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-03-12 05:00:32 +00:00
|
|
|
static void
|
2012-06-20 14:13:12 -05:00
|
|
|
printnodename(dns_rbtnode_t *node) {
|
2008-01-22 23:28:04 +00:00
|
|
|
isc_region_t r;
|
|
|
|
dns_name_t name;
|
|
|
|
char buffer[DNS_NAME_FORMATSIZE];
|
|
|
|
dns_offsets_t offsets;
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
r.length = NAMELEN(node);
|
|
|
|
r.base = NAME(node);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_name_init(&name, offsets);
|
|
|
|
dns_name_fromregion(&name, &r);
|
1999-01-20 10:13:43 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_name_format(&name, buffer, sizeof(buffer));
|
2000-07-31 22:34:01 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
printf("\"%s\"", buffer);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-03-12 05:00:32 +00:00
|
|
|
static void
|
2012-06-20 14:13:12 -05:00
|
|
|
dns_rbt_printtree(dns_rbtnode_t *root, dns_rbtnode_t *parent,
|
|
|
|
int depth, const char *direction,
|
|
|
|
void (*data_printer)(FILE *, void *))
|
|
|
|
{
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbt_indent(depth);
|
|
|
|
|
|
|
|
if (root != NULL) {
|
2012-06-20 14:13:12 -05:00
|
|
|
printnodename(root);
|
|
|
|
printf(" (%s, %s", direction, IS_RED(root) ? "RED" : "BLACK");
|
2008-01-22 23:28:04 +00:00
|
|
|
|
|
|
|
if ((! IS_ROOT(root) && PARENT(root) != parent) ||
|
|
|
|
( IS_ROOT(root) && depth > 0 &&
|
|
|
|
DOWN(PARENT(root)) != root)) {
|
|
|
|
|
|
|
|
printf(" (BAD parent pointer! -> ");
|
|
|
|
if (PARENT(root) != NULL)
|
2012-06-20 14:13:12 -05:00
|
|
|
printnodename(PARENT(root));
|
2008-01-22 23:28:04 +00:00
|
|
|
else
|
|
|
|
printf("NULL");
|
|
|
|
printf(")");
|
|
|
|
}
|
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
printf(")");
|
2012-06-20 23:46:40 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
if (root->data != NULL && data_printer != NULL) {
|
|
|
|
printf(" data@%p: ", root->data);
|
|
|
|
data_printer(stdout, root->data);
|
|
|
|
}
|
|
|
|
printf("\n");
|
2008-01-22 23:28:04 +00:00
|
|
|
|
|
|
|
depth++;
|
|
|
|
|
|
|
|
if (IS_RED(root) && IS_RED(LEFT(root)))
|
2012-06-20 14:13:12 -05:00
|
|
|
printf("** Red/Red color violation on left\n");
|
|
|
|
dns_rbt_printtree(LEFT(root), root, depth, "left",
|
|
|
|
data_printer);
|
2008-01-22 23:28:04 +00:00
|
|
|
|
|
|
|
if (IS_RED(root) && IS_RED(RIGHT(root)))
|
2012-06-20 14:13:12 -05:00
|
|
|
printf("** Red/Red color violation on right\n");
|
|
|
|
dns_rbt_printtree(RIGHT(root), root, depth, "right",
|
|
|
|
data_printer);
|
2008-01-22 23:28:04 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
dns_rbt_printtree(DOWN(root), NULL, depth, "down",
|
|
|
|
data_printer);
|
|
|
|
} else {
|
|
|
|
printf("NULL (%s)\n", direction);
|
|
|
|
}
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
|
|
|
|
1999-01-25 15:46:30 +00:00
|
|
|
void
|
2012-06-20 14:13:12 -05:00
|
|
|
dns_rbt_printall(dns_rbt_t *rbt, void (*data_printer)(FILE *, void *)) {
|
2008-01-22 23:28:04 +00:00
|
|
|
REQUIRE(VALID_RBT(rbt));
|
1999-03-12 05:00:32 +00:00
|
|
|
|
2012-06-20 14:13:12 -05:00
|
|
|
dns_rbt_printtree(rbt->root, NULL, 0, "root", data_printer);
|
1999-01-20 10:13:43 +00:00
|
|
|
}
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Chain Functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
dns_rbtnodechain_init(dns_rbtnodechain_t *chain, isc_mem_t *mctx) {
|
2008-01-22 01:34:15 +00:00
|
|
|
/*
|
|
|
|
* Initialize 'chain'.
|
|
|
|
*/
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
|
2008-01-22 01:34:15 +00:00
|
|
|
REQUIRE(chain != NULL);
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
|
2008-01-22 01:34:15 +00:00
|
|
|
chain->mctx = mctx;
|
|
|
|
chain->end = NULL;
|
|
|
|
chain->level_count = 0;
|
|
|
|
chain->level_matches = 0;
|
|
|
|
memset(chain->levels, 0, sizeof(chain->levels));
|
1999-04-09 15:21:15 +00:00
|
|
|
|
2008-01-22 01:34:15 +00:00
|
|
|
chain->magic = CHAIN_MAGIC;
|
1999-04-09 15:21:15 +00:00
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t
|
The ADD_ANCESTOR macro now checks for the required memory, since it is used
in a lot of places now. It _returns_ if it fails to get the necessary memory,
so any function using it should be returning a dns_result_t.
chain_name moved toward start of file, for inlining by findnode.
move_chain_to_last broke out some functionality of dns_rbtnodechain_last
(which now calls it), also for the benefit of findnode.
dns_rbtnodechain_init on the chain used by addnode.
All of the new_foundname concatenation gook was removed from findnode,
which now just sets foundname by using chain_name.
The chain set up by findnode points to the predecessor when the searched
for name is not found.
nodechain_current was changed to take name, origin and node parameters while
returning a dns_result_t. This allows the chain returned by findnode to
be named.
The nodechain_{first,last,prev} use nodechain_current to set their
names and origins. nodechain_next does not need it because it will never
have to do the "set the origin to '.' and remove '.' from names in the
root level" bit. The root level will never be more than one name, and
everything in the megatree is a successor to that node, so 'next' never
reaches the root level.
1999-04-16 16:12:15 +00:00
|
|
|
dns_rbtnodechain_current(dns_rbtnodechain_t *chain, dns_name_t *name,
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_name_t *origin, dns_rbtnode_t **node)
|
2000-07-31 23:27:25 +00:00
|
|
|
{
|
2008-01-22 23:28:04 +00:00
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
|
|
|
|
|
|
REQUIRE(VALID_CHAIN(chain));
|
|
|
|
|
|
|
|
if (node != NULL)
|
|
|
|
*node = chain->end;
|
|
|
|
|
|
|
|
if (chain->end == NULL)
|
|
|
|
return (ISC_R_NOTFOUND);
|
|
|
|
|
|
|
|
if (name != NULL) {
|
|
|
|
NODENAME(chain->end, name);
|
|
|
|
|
|
|
|
if (chain->level_count == 0) {
|
|
|
|
/*
|
|
|
|
* Names in the top level tree are all absolute.
|
|
|
|
* Always make 'name' relative.
|
|
|
|
*/
|
|
|
|
INSIST(dns_name_isabsolute(name));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is cheaper than dns_name_getlabelsequence().
|
|
|
|
*/
|
|
|
|
name->labels--;
|
|
|
|
name->length--;
|
|
|
|
name->attributes &= ~DNS_NAMEATTR_ABSOLUTE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (origin != NULL) {
|
|
|
|
if (chain->level_count > 0)
|
|
|
|
result = chain_name(chain, origin, ISC_FALSE);
|
|
|
|
else
|
|
|
|
result = dns_name_copy(dns_rootname, origin, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (result);
|
1999-04-09 15:21:15 +00:00
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t
|
1999-04-09 15:21:15 +00:00
|
|
|
dns_rbtnodechain_prev(dns_rbtnodechain_t *chain, dns_name_t *name,
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_name_t *origin)
|
1999-04-09 15:21:15 +00:00
|
|
|
{
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnode_t *current, *previous, *predecessor;
|
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
|
|
isc_boolean_t new_origin = ISC_FALSE;
|
|
|
|
|
|
|
|
REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
|
|
|
|
|
|
|
|
predecessor = NULL;
|
|
|
|
|
|
|
|
current = chain->end;
|
|
|
|
|
|
|
|
if (LEFT(current) != NULL) {
|
|
|
|
/*
|
|
|
|
* Moving left one then right as far as possible is the
|
|
|
|
* previous node, at least for this level.
|
|
|
|
*/
|
|
|
|
current = LEFT(current);
|
|
|
|
|
|
|
|
while (RIGHT(current) != NULL)
|
|
|
|
current = RIGHT(current);
|
|
|
|
|
|
|
|
predecessor = current;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* No left links, so move toward the root. If at any point on
|
|
|
|
* the way there the link from parent to child is a right
|
|
|
|
* link, then the parent is the previous node, at least
|
|
|
|
* for this level.
|
|
|
|
*/
|
|
|
|
while (! IS_ROOT(current)) {
|
|
|
|
previous = current;
|
|
|
|
current = PARENT(current);
|
|
|
|
|
|
|
|
if (RIGHT(current) == previous) {
|
|
|
|
predecessor = current;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (predecessor != NULL) {
|
|
|
|
/*
|
|
|
|
* Found a predecessor node in this level. It might not
|
|
|
|
* really be the predecessor, however.
|
|
|
|
*/
|
|
|
|
if (DOWN(predecessor) != NULL) {
|
|
|
|
/*
|
|
|
|
* The predecessor is really down at least one level.
|
|
|
|
* Go down and as far right as possible, and repeat
|
|
|
|
* as long as the rightmost node has a down pointer.
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
/*
|
|
|
|
* XXX DCL Need to do something about origins
|
|
|
|
* here. See whether to go down, and if so
|
|
|
|
* whether it is truly what Bob calls a
|
|
|
|
* new origin.
|
|
|
|
*/
|
|
|
|
ADD_LEVEL(chain, predecessor);
|
|
|
|
predecessor = DOWN(predecessor);
|
|
|
|
|
|
|
|
/* XXX DCL duplicated from above; clever
|
|
|
|
* way to unduplicate? */
|
|
|
|
|
|
|
|
while (RIGHT(predecessor) != NULL)
|
|
|
|
predecessor = RIGHT(predecessor);
|
|
|
|
} while (DOWN(predecessor) != NULL);
|
|
|
|
|
|
|
|
/* XXX DCL probably needs work on the concept */
|
|
|
|
if (origin != NULL)
|
|
|
|
new_origin = ISC_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (chain->level_count > 0) {
|
|
|
|
/*
|
|
|
|
* Dang, didn't find a predecessor in this level.
|
|
|
|
* Got to the root of this level without having traversed
|
|
|
|
* any right links. Ascend the tree one level; the
|
|
|
|
* node that points to this tree is the predecessor.
|
|
|
|
*/
|
|
|
|
INSIST(chain->level_count > 0 && IS_ROOT(current));
|
|
|
|
predecessor = chain->levels[--chain->level_count];
|
|
|
|
|
|
|
|
/* XXX DCL probably needs work on the concept */
|
|
|
|
/*
|
|
|
|
* Don't declare an origin change when the new origin is "."
|
|
|
|
* at the top level tree, because "." is declared as the origin
|
|
|
|
* for the second level tree.
|
|
|
|
*/
|
|
|
|
if (origin != NULL &&
|
|
|
|
(chain->level_count > 0 || OFFSETLEN(predecessor) > 1))
|
|
|
|
new_origin = ISC_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (predecessor != NULL) {
|
|
|
|
chain->end = predecessor;
|
|
|
|
|
|
|
|
if (new_origin) {
|
|
|
|
result = dns_rbtnodechain_current(chain, name, origin,
|
|
|
|
NULL);
|
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
result = DNS_R_NEWORIGIN;
|
|
|
|
|
|
|
|
} else
|
|
|
|
result = dns_rbtnodechain_current(chain, name, NULL,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
} else
|
|
|
|
result = ISC_R_NOMORE;
|
|
|
|
|
|
|
|
return (result);
|
1999-04-09 15:21:15 +00:00
|
|
|
}
|
|
|
|
|
2008-09-24 02:46:23 +00:00
|
|
|
isc_result_t
|
|
|
|
dns_rbtnodechain_down(dns_rbtnodechain_t *chain, dns_name_t *name,
|
|
|
|
dns_name_t *origin)
|
|
|
|
{
|
|
|
|
dns_rbtnode_t *current, *successor;
|
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
|
|
isc_boolean_t new_origin = ISC_FALSE;
|
|
|
|
|
|
|
|
REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
|
|
|
|
|
|
|
|
successor = NULL;
|
|
|
|
|
|
|
|
current = chain->end;
|
|
|
|
|
|
|
|
if (DOWN(current) != NULL) {
|
|
|
|
/*
|
|
|
|
* Don't declare an origin change when the new origin is "."
|
|
|
|
* at the second level tree, because "." is already declared
|
|
|
|
* as the origin for the top level tree.
|
|
|
|
*/
|
|
|
|
if (chain->level_count > 0 ||
|
|
|
|
OFFSETLEN(current) > 1)
|
|
|
|
new_origin = ISC_TRUE;
|
|
|
|
|
|
|
|
ADD_LEVEL(chain, current);
|
|
|
|
current = DOWN(current);
|
|
|
|
|
|
|
|
while (LEFT(current) != NULL)
|
|
|
|
current = LEFT(current);
|
|
|
|
|
|
|
|
successor = current;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (successor != NULL) {
|
|
|
|
chain->end = successor;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It is not necessary to use dns_rbtnodechain_current like
|
|
|
|
* the other functions because this function will never
|
|
|
|
* find a node in the topmost level. This is because the
|
|
|
|
* root level will never be more than one name, and everything
|
|
|
|
* in the megatree is a successor to that node, down at
|
|
|
|
* the second level or below.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (name != NULL)
|
|
|
|
NODENAME(chain->end, name);
|
|
|
|
|
|
|
|
if (new_origin) {
|
|
|
|
if (origin != NULL)
|
|
|
|
result = chain_name(chain, origin, ISC_FALSE);
|
|
|
|
|
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
result = DNS_R_NEWORIGIN;
|
|
|
|
|
|
|
|
} else
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
|
|
|
|
} else
|
|
|
|
result = ISC_R_NOMORE;
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
isc_result_t
|
|
|
|
dns_rbtnodechain_nextflat(dns_rbtnodechain_t *chain, dns_name_t *name) {
|
|
|
|
dns_rbtnode_t *current, *previous, *successor;
|
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
|
|
|
|
|
|
REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
|
|
|
|
|
|
|
|
successor = NULL;
|
|
|
|
|
|
|
|
current = chain->end;
|
|
|
|
|
|
|
|
if (RIGHT(current) == NULL) {
|
|
|
|
while (! IS_ROOT(current)) {
|
|
|
|
previous = current;
|
|
|
|
current = PARENT(current);
|
|
|
|
|
|
|
|
if (LEFT(current) == previous) {
|
|
|
|
successor = current;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
current = RIGHT(current);
|
|
|
|
|
|
|
|
while (LEFT(current) != NULL)
|
|
|
|
current = LEFT(current);
|
|
|
|
|
|
|
|
successor = current;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (successor != NULL) {
|
|
|
|
chain->end = successor;
|
|
|
|
|
|
|
|
if (name != NULL)
|
|
|
|
NODENAME(chain->end, name);
|
|
|
|
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
} else
|
|
|
|
result = ISC_R_NOMORE;
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t
|
1999-04-09 15:21:15 +00:00
|
|
|
dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name,
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_name_t *origin)
|
1999-04-09 15:21:15 +00:00
|
|
|
{
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnode_t *current, *previous, *successor;
|
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
|
|
isc_boolean_t new_origin = ISC_FALSE;
|
|
|
|
|
|
|
|
REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
|
|
|
|
|
|
|
|
successor = NULL;
|
|
|
|
|
|
|
|
current = chain->end;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there is a level below this node, the next node is the leftmost
|
|
|
|
* node of the next level.
|
|
|
|
*/
|
|
|
|
if (DOWN(current) != NULL) {
|
|
|
|
/*
|
|
|
|
* Don't declare an origin change when the new origin is "."
|
|
|
|
* at the second level tree, because "." is already declared
|
|
|
|
* as the origin for the top level tree.
|
|
|
|
*/
|
|
|
|
if (chain->level_count > 0 ||
|
|
|
|
OFFSETLEN(current) > 1)
|
|
|
|
new_origin = ISC_TRUE;
|
|
|
|
|
|
|
|
ADD_LEVEL(chain, current);
|
|
|
|
current = DOWN(current);
|
|
|
|
|
|
|
|
while (LEFT(current) != NULL)
|
|
|
|
current = LEFT(current);
|
|
|
|
|
|
|
|
successor = current;
|
|
|
|
|
|
|
|
} else if (RIGHT(current) == NULL) {
|
|
|
|
/*
|
|
|
|
* The successor is up, either in this level or a previous one.
|
|
|
|
* Head back toward the root of the tree, looking for any path
|
|
|
|
* that was via a left link; the successor is the node that has
|
|
|
|
* that left link. In the event the root of the level is
|
|
|
|
* reached without having traversed any left links, ascend one
|
|
|
|
* level and look for either a right link off the point of
|
|
|
|
* ascent, or search for a left link upward again, repeating
|
2009-01-17 14:45:17 +00:00
|
|
|
* ascends until either case is true.
|
2008-01-22 23:28:04 +00:00
|
|
|
*/
|
|
|
|
do {
|
|
|
|
while (! IS_ROOT(current)) {
|
|
|
|
previous = current;
|
|
|
|
current = PARENT(current);
|
|
|
|
|
|
|
|
if (LEFT(current) == previous) {
|
|
|
|
successor = current;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (successor == NULL) {
|
|
|
|
/*
|
|
|
|
* Reached the root without having traversed
|
|
|
|
* any left pointers, so this level is done.
|
|
|
|
*/
|
|
|
|
if (chain->level_count == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
current = chain->levels[--chain->level_count];
|
|
|
|
new_origin = ISC_TRUE;
|
|
|
|
|
|
|
|
if (RIGHT(current) != NULL)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (successor == NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (successor == NULL && RIGHT(current) != NULL) {
|
|
|
|
current = RIGHT(current);
|
|
|
|
|
|
|
|
while (LEFT(current) != NULL)
|
|
|
|
current = LEFT(current);
|
|
|
|
|
|
|
|
successor = current;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (successor != NULL) {
|
|
|
|
chain->end = successor;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It is not necessary to use dns_rbtnodechain_current like
|
|
|
|
* the other functions because this function will never
|
|
|
|
* find a node in the topmost level. This is because the
|
|
|
|
* root level will never be more than one name, and everything
|
|
|
|
* in the megatree is a successor to that node, down at
|
|
|
|
* the second level or below.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (name != NULL)
|
|
|
|
NODENAME(chain->end, name);
|
|
|
|
|
|
|
|
if (new_origin) {
|
|
|
|
if (origin != NULL)
|
|
|
|
result = chain_name(chain, origin, ISC_FALSE);
|
|
|
|
|
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
result = DNS_R_NEWORIGIN;
|
|
|
|
|
|
|
|
} else
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
|
|
|
|
} else
|
|
|
|
result = ISC_R_NOMORE;
|
|
|
|
|
|
|
|
return (result);
|
1999-04-09 15:21:15 +00:00
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t
|
1999-04-09 15:21:15 +00:00
|
|
|
dns_rbtnodechain_first(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_name_t *name, dns_name_t *origin)
|
1999-04-09 15:21:15 +00:00
|
|
|
|
|
|
|
{
|
2008-01-22 23:28:04 +00:00
|
|
|
isc_result_t result;
|
1999-04-09 15:21:15 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
REQUIRE(VALID_CHAIN(chain));
|
1999-04-14 12:03:18 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnodechain_reset(chain);
|
1999-04-14 12:03:18 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
chain->end = rbt->root;
|
1999-04-14 12:03:18 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
result = dns_rbtnodechain_current(chain, name, origin, NULL);
|
1999-04-14 12:03:18 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
result = DNS_R_NEWORIGIN;
|
1999-04-14 12:03:18 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
return (result);
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
}
|
|
|
|
|
1999-12-23 00:09:04 +00:00
|
|
|
isc_result_t
|
1999-04-09 15:21:15 +00:00
|
|
|
dns_rbtnodechain_last(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_name_t *name, dns_name_t *origin)
|
1999-04-09 15:21:15 +00:00
|
|
|
|
|
|
|
{
|
2008-01-22 23:28:04 +00:00
|
|
|
isc_result_t result;
|
1999-04-09 15:21:15 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
REQUIRE(VALID_RBT(rbt));
|
|
|
|
REQUIRE(VALID_CHAIN(chain));
|
1999-04-14 12:03:18 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnodechain_reset(chain);
|
1999-04-14 12:03:18 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
result = move_chain_to_last(chain, rbt->root);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (result);
|
1999-04-14 12:03:18 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
result = dns_rbtnodechain_current(chain, name, origin, NULL);
|
1999-04-14 12:03:18 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
result = DNS_R_NEWORIGIN;
|
1999-04-14 12:03:18 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
return (result);
|
1999-04-09 15:21:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
void
|
|
|
|
dns_rbtnodechain_reset(dns_rbtnodechain_t *chain) {
|
2008-01-22 23:28:04 +00:00
|
|
|
/*
|
|
|
|
* Free any dynamic storage associated with 'chain', and then
|
|
|
|
* reinitialize 'chain'.
|
|
|
|
*/
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
REQUIRE(VALID_CHAIN(chain));
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
chain->end = NULL;
|
|
|
|
chain->level_count = 0;
|
|
|
|
chain->level_matches = 0;
|
Changes from Bob, best described in his own barely-edited words:
I added support for the full search algorithm to the RBT database
code. The database has some special needs in searching, in particular
certain nodes need to be examined before the DOWN pointer is followed.
I solved this problem by adding a 'callback' bit to the node structure.
When findnode is called, the caller can supply a callback. Before we go
DOWN at a node, we call the callback if the callback bit is set at the
node. If the callback returns DNS_R_CONTINUE, we proceed. Otherwise, we
will stop right there and return what the caller said to return. I
added an 'empty_data_ok' flag to findnode as well, so that nodes with
empty data can be made candidates for partial match status.
I also wanted to make dns_rbtnodechain_t public, so that a chain could
be allocated on the stack by the caller. I added two routines,
dns_rbtnodechain_init() and dns_rbtnodechain_reset() to work with them.
The latter routine frees any dynamically allocated memory associated with
the chain. The chain structure now contains the memory context. I also
moved get_ancestor_mem() and put_ancestor_mem() up in the file, so that
inlining could be more effective.
In the nits department, you wrote
*node = result == DNS_R_SUCCESS ? current : NULL;
In the future, please write this instead (the patch has this change in it):
if (result == DNS_R_SUCCESS)
*node = current;
else
*node = NULL;
1999-04-01 03:15:54 +00:00
|
|
|
}
|
1999-04-09 15:21:15 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
dns_rbtnodechain_invalidate(dns_rbtnodechain_t *chain) {
|
2008-01-22 23:28:04 +00:00
|
|
|
/*
|
|
|
|
* Free any dynamic storage associated with 'chain', and then
|
|
|
|
* invalidate 'chain'.
|
|
|
|
*/
|
1999-04-09 15:21:15 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
dns_rbtnodechain_reset(chain);
|
1999-04-09 15:21:15 +00:00
|
|
|
|
2008-01-22 23:28:04 +00:00
|
|
|
chain->magic = 0;
|
1999-04-09 15:21:15 +00:00
|
|
|
}
|