[35904] Add various RBT unit tests

No CHANGES entry was added as this commit mainly adds tests related
code.

Squashed commit of the following:

commit d3d44508daa128fb8b60f64b3a8c81f80602273d
Author: Evan Hunt <each@isc.org>
Date:   Wed May 7 09:36:41 2014 -0700

    [rt35904] remove private non-static names from .def file

commit dbca45661c3939f21c3bb3f405d08cfe1b35d7aa
Author: Mukund Sivaraman <muks@isc.org>
Date:   Wed May 7 21:39:32 2014 +0530

    Remove test for shortcut findnode()

    The implementation was not included in this review branch, but the tests
    erroneously made it through.

    This functionality will be addressed in a different ticket (RT#35906).

commit 94ff14576ab3407f2612d34727b7eacfefc3668c
Author: Mukund Sivaraman <muks@isc.org>
Date:   Wed May 7 21:36:50 2014 +0530

    Minor indent fix

commit 50972f17697bb222996e433faa8224843366f9b2
Author: Evan Hunt <each@isc.org>
Date:   Tue May 6 20:05:21 2014 -0700

    [rt35904] style

commit 5c4d5d41fcc5bfecdeebc008896974385c841b8d
Author: Mukund Sivaraman <muks@isc.org>
Date:   Sun May 4 19:19:36 2014 +0530

    RBT related updates

    * Add various RBT unit tests
    * Add some helper methods useful in unit testing RBT code
    * General cleanup
This commit is contained in:
Mukund Sivaraman
2014-05-29 11:01:57 +05:30
parent 4694229f60
commit ce376a81fa
8 changed files with 2042 additions and 394 deletions

View File

@@ -726,10 +726,14 @@ dns_rbt_deserialize_tree(void *base_address, size_t filesize,
*/
void
dns_rbt_printall(dns_rbt_t *rbt, void (*data_printer)(FILE *, void *));
dns_rbt_printtext(dns_rbt_t *rbt,
void (*data_printer)(FILE *, void *), FILE *f);
/*%<
* Print an ASCII representation of the internal structure of the red-black
* tree of trees.
* tree of trees to the passed stream.
*
* data_printer is a callback function that is called to print the data
* in a node. It should print it to the passed FILE stream.
*
* Notes:
* \li The name stored at each node, along with the node's color, is printed.
@@ -739,9 +743,69 @@ dns_rbt_printall(dns_rbt_t *rbt, void (*data_printer)(FILE *, void *));
*/
void
dns_rbt_printnodeinfo(dns_rbtnode_t *n);
dns_rbt_printdot(dns_rbt_t *rbt, isc_boolean_t show_pointers, FILE *f);
/*%<
* Print a GraphViz dot representation of the internal structure of the
* red-black tree of trees to the passed stream.
*
* If show_pointers is TRUE, pointers are also included in the generated
* graph.
*
* Notes:
* \li The name stored at each node, along with the node's color is displayed.
* Then the down pointer, left and right pointers are displayed
* recursively in turn. NULL left, right and down pointers are
* silently omitted.
*/
void
dns_rbt_printnodeinfo(dns_rbtnode_t *n, FILE *f);
/*%<
* Print out various information about a node
*
* Requires:
*\li 'n' is a valid pointer.
*
*\li 'f' points to a valid open FILE structure that allows writing.
*/
size_t
dns__rbt_getheight(dns_rbt_t *rbt);
/*%<
* Return the maximum height of sub-root nodes found in the red-black
* forest.
*
* The height of a node is defined as the number of nodes in the longest
* path from the node to a leaf. For each subtree in the forest, this
* function determines the height of its root node. Then it returns the
* maximum such height in the forest.
*
* Note: This function exists for testing purposes. Non-test code must
* not use it.
*
* Requires:
* \li rbt is a valid rbt manager.
*/
isc_boolean_t
dns__rbt_checkproperties(dns_rbt_t *rbt);
/*%<
* Check red-black properties of the forest.
*
* Note: This function exists for testing purposes. Non-test code must
* not use it.
*
* Requires:
* \li rbt is a valid rbt manager.
*/
size_t
dns__rbtnode_getdistance(dns_rbtnode_t *node);
/*%<
* Return the distance (in nodes) from the node to its upper node of its
* subtree. The root node has a distance of 1. A child of the root node
* has a distance of 2.
*/
/*****
@@ -1036,6 +1100,12 @@ dns_rbtnode_refdecrement(dns_rbtnode_t *node, unsigned int *refs) {
#endif
#endif /* DNS_RBT_USEISCREFCOUNT */
void
dns_rbtnode_nodename(dns_rbtnode_t *node, dns_name_t *name);
dns_rbtnode_t *
dns_rbt_root(dns_rbt_t *rbt);
ISC_LANG_ENDDECLS
#endif /* DNS_RBT_H */

View File

@@ -209,6 +209,7 @@ getdata(dns_rbtnode_t *node, file_header_t *header) {
#define RIGHT(node) ((node)->right)
#define DOWN(node) ((node)->down)
#define DATA(node) ((node)->data)
#define IS_EMPTY(node) ((node)->data == NULL)
#define HASHNEXT(node) ((node)->hashnext)
#define HASHVAL(node) ((node)->hashval)
#define COLOR(node) ((node)->color)
@@ -280,6 +281,21 @@ NODENAME(dns_rbtnode_t *node, dns_name_t *name) {
name->attributes |= DNS_NAMEATTR_READONLY;
}
void
dns_rbtnode_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;
}
dns_rbtnode_t *
dns_rbt_root(dns_rbt_t *rbt) {
return rbt->root;
}
#ifdef DNS_RBT_USEHASH
static isc_result_t
inithash(dns_rbt_t *rbt);
@@ -302,8 +318,6 @@ Name(dns_rbtnode_t *node) {
return (name);
}
static void printnodename(dns_rbtnode_t *node);
static void
hexdump(const char *desc, unsigned char *data, size_t size) {
char hexdump[BUFSIZ * 2 + 1];
@@ -326,23 +340,47 @@ hexdump(const char *desc, unsigned char *data, size_t size) {
} while (size > 0);
fprintf(stderr, "\n");
}
#endif
#endif /* DEBUG */
/* The passed node must not be NULL. */
static inline dns_rbtnode_t *
find_up(dns_rbtnode_t *node) {
dns_rbtnode_t *root;
get_subtree_root(dns_rbtnode_t *node) {
while (!IS_ROOT(node)) {
node = PARENT(node);
}
return (node);
}
/* Upper node is the parent of the root of the passed node's
* subtree. The passed node must not be NULL.
*/
static inline dns_rbtnode_t *
get_upper_node(dns_rbtnode_t *node) {
dns_rbtnode_t *root = get_subtree_root(node);
/*
* 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. */
return (PARENT(root));
}
size_t
dns__rbtnode_getdistance(dns_rbtnode_t *node) {
size_t nodes = 1;
while (node != NULL) {
if (IS_ROOT(node))
break;
nodes++;
node = PARENT(node);
}
return (nodes);
}
/*
* Forward declarations.
*/
@@ -387,7 +425,7 @@ static void
deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, dns_rbtnode_t **nodep);
static void
printnodename(dns_rbtnode_t *node);
printnodename(dns_rbtnode_t *node, isc_boolean_t quoted, FILE *f);
static void
freenode(dns_rbt_t *rbt, dns_rbtnode_t **nodep);
@@ -1371,15 +1409,14 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
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;
order = 0;
}
/*
* Appease GCC about variables it incorrectly thinks are
* possibly used uninitialized.
*/
compared = dns_namereln_none;
last_compared = NULL;
order = 0;
dns_fixedname_init(&fixedcallbackname);
callback_name = dns_fixedname_name(&fixedcallbackname);
@@ -1405,6 +1442,12 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
NODENAME(current, &current_name);
compared = dns_name_fullcompare(search_name, &current_name,
&order, &common_labels);
/*
* last_compared is used as a shortcut to start (or
* continue rather) finding the stop-node of the search
* when hashing was used (see much below in this
* function).
*/
last_compared = current;
if (compared == dns_namereln_equal)
@@ -1412,6 +1455,16 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
if (compared == dns_namereln_none) {
#ifdef DNS_RBT_USEHASH
/*
* Here, current is pointing at a subtree root
* node. We try to find a matching node using
* the hashtable. We can get one of 3 results
* here: (a) we locate the matching node, (b) we
* find a node to which the current node has a
* subdomain relation, (c) we fail to find (a)
* or (b).
*/
dns_name_t hash_name;
dns_rbtnode_t *hnode;
dns_rbtnode_t *up_current;
@@ -1446,7 +1499,12 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
hashagain:
/*
* Hash includes tail.
* Compute the hash over the full absolute
* name. Look for the smallest suffix match at
* this tree level (hlevel), and then at every
* iteration, look for the next smallest suffix
* match (add another subdomain label to the
* absolute name being hashed).
*/
dns_name_getlabelsequence(name,
nlabels - tlabels,
@@ -1457,6 +1515,10 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
nlabels - tlabels,
tlabels, &hash_name);
/*
* Walk all the nodes in the hash bucket pointed
* by the computed hash value.
*/
for (hnode = rbt->hashtable[hash % rbt->hashsize];
hnode != NULL;
hnode = hnode->hashnext)
@@ -1465,8 +1527,16 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
if (hash != HASHVAL(hnode))
continue;
if (find_up(hnode) != up_current)
/*
* This checks that the hashed label
* sequence being looked up is at the
* same tree level, so that we don't
* match a labelsequence from some other
* subdomain.
*/
if (get_upper_node(hnode) != up_current)
continue;
dns_name_init(&hnode_name, NULL);
NODENAME(hnode, &hnode_name);
if (dns_name_equal(&hnode_name, &hash_name))
@@ -1971,7 +2041,7 @@ dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, isc_boolean_t recurse)
* deleted. If the deleted node is the top level, parent will be set
* to NULL.
*/
parent = find_up(node);
parent = get_upper_node(node);
/*
* This node now has no down pointer (either because it didn't
@@ -2051,7 +2121,7 @@ dns_rbt_fullnamefromnode(dns_rbtnode_t *node, dns_name_t *name) {
if (result != ISC_R_SUCCESS)
break;
node = find_up(node);
node = get_upper_node(node);
} while (! dns_name_isabsolute(name));
return (result);
@@ -2799,42 +2869,140 @@ deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, dns_rbtnode_t **nodep) {
goto again;
}
static size_t
getheight_helper(dns_rbtnode_t *node) {
size_t dl, dr;
size_t this_height, down_height;
if (node == NULL)
return (0);
dl = getheight_helper(LEFT(node));
dr = getheight_helper(RIGHT(node));
this_height = ISC_MAX(dl + 1, dr + 1);
down_height = getheight_helper(DOWN(node));
return (ISC_MAX(this_height, down_height));
}
size_t
dns__rbt_getheight(dns_rbt_t *rbt) {
return (getheight_helper(rbt->root));
}
static isc_boolean_t
check_properties_helper(dns_rbtnode_t *node) {
if (node == NULL)
return (ISC_TRUE);
if (IS_RED(node)) {
/* Root nodes must be BLACK. */
if (IS_ROOT(node))
return (ISC_FALSE);
/* Both children of RED nodes must be BLACK. */
if (IS_RED(LEFT(node)) || IS_RED(RIGHT(node)))
return (ISC_FALSE);
}
/* If node is assigned to the down_ pointer of its parent, it is
* a subtree root and must have the flag set.
*/
if (((!PARENT(node)) ||
(DOWN(PARENT(node)) == node)) &&
(!IS_ROOT(node)))
{
return (ISC_FALSE);
}
/* Repeat tests with this node's children. */
return (check_properties_helper(LEFT(node)) &&
check_properties_helper(RIGHT(node)) &&
check_properties_helper(DOWN(node)));
}
static isc_boolean_t
check_black_distance_helper(dns_rbtnode_t *node, size_t *distance) {
size_t dl, dr, dd;
if (node == NULL) {
*distance = 1;
return (ISC_TRUE);
}
if (!check_black_distance_helper(LEFT(node), &dl))
return (ISC_FALSE);
if (!check_black_distance_helper(RIGHT(node), &dr))
return (ISC_FALSE);
if (!check_black_distance_helper(DOWN(node), &dd))
return (ISC_FALSE);
/* Left and right side black node counts must match. */
if (dl != dr)
return (ISC_FALSE);
if (IS_BLACK(node))
dl++;
*distance = dl;
return (ISC_TRUE);
}
isc_boolean_t
dns__rbt_checkproperties(dns_rbt_t *rbt) {
size_t dd;
if (!check_properties_helper(rbt->root))
return (ISC_FALSE);
/* Path from a given node to all its leaves must contain the
* same number of BLACK child nodes. This is done separately
* here instead of inside check_properties_helper() as
* it would take (n log n) complexity otherwise.
*/
return (check_black_distance_helper(rbt->root, &dd));
}
static void
dns_rbt_indent(int depth) {
dns_rbt_indent(FILE *f, int depth) {
int i;
printf("%4d ", depth);
fprintf(f, "%4d ", depth);
for (i = 0; i < depth; i++)
printf("- ");
fprintf(f, "- ");
}
void
dns_rbt_printnodeinfo(dns_rbtnode_t *n) {
printf("Node info for nodename: ");
printnodename(n);
printf("\n");
dns_rbt_printnodeinfo(dns_rbtnode_t *n, FILE *f) {
fprintf(f, "Node info for nodename: ");
printnodename(n, ISC_TRUE, f);
fprintf(f, "\n");
printf("n = %p\n", n);
fprintf(f, "n = %p\n", n);
printf("Relative pointers: %s%s%s%s%s\n",
fprintf(f, "Relative pointers: %s%s%s%s%s\n",
(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);
fprintf(f, "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);
fprintf(f, "Parent: %p\n", n->parent);
fprintf(f, "Right: %p\n", n->right);
fprintf(f, "Left: %p\n", n->left);
fprintf(f, "Down: %p\n", n->down);
fprintf(f, "daTa: %p\n", n->data);
}
static void
printnodename(dns_rbtnode_t *node) {
printnodename(dns_rbtnode_t *node, isc_boolean_t quoted, FILE *f) {
isc_region_t r;
dns_name_t name;
char buffer[DNS_NAME_FORMATSIZE];
@@ -2848,65 +3016,135 @@ printnodename(dns_rbtnode_t *node) {
dns_name_format(&name, buffer, sizeof(buffer));
printf("\"%s\"", buffer);
if (quoted)
fprintf(f, "\"%s\"", buffer);
else
fprintf(f, "%s", buffer);
}
static void
dns_rbt_printtree(dns_rbtnode_t *root, dns_rbtnode_t *parent,
print_text_helper(dns_rbtnode_t *root, dns_rbtnode_t *parent,
int depth, const char *direction,
void (*data_printer)(FILE *, void *))
void (*data_printer)(FILE *, void *), FILE *f)
{
dns_rbt_indent(depth);
dns_rbt_indent(f, depth);
if (root != NULL) {
printnodename(root);
printf(" (%s, %s", direction, IS_RED(root) ? "RED" : "BLACK");
printnodename(root, ISC_TRUE, f);
fprintf(f, " (%s, %s", direction,
IS_RED(root) ? "RED" : "BLACK");
if ((! IS_ROOT(root) && PARENT(root) != parent) ||
( IS_ROOT(root) && depth > 0 &&
DOWN(PARENT(root)) != root)) {
printf(" (BAD parent pointer! -> ");
fprintf(f, " (BAD parent pointer! -> ");
if (PARENT(root) != NULL)
printnodename(PARENT(root));
printnodename(PARENT(root), ISC_TRUE, f);
else
printf("NULL");
printf(")");
fprintf(f, "NULL");
fprintf(f, ")");
}
printf(")");
fprintf(f, ")");
if (root->data != NULL && data_printer != NULL) {
printf(" data@%p: ", root->data);
data_printer(stdout, root->data);
fprintf(f, " data@%p: ", root->data);
data_printer(f, root->data);
}
printf("\n");
fprintf(f, "\n");
depth++;
if (IS_RED(root) && IS_RED(LEFT(root)))
printf("** Red/Red color violation on left\n");
dns_rbt_printtree(LEFT(root), root, depth, "left",
data_printer);
fprintf(f, "** Red/Red color violation on left\n");
print_text_helper(LEFT(root), root, depth, "left",
data_printer, f);
if (IS_RED(root) && IS_RED(RIGHT(root)))
printf("** Red/Red color violation on right\n");
dns_rbt_printtree(RIGHT(root), root, depth, "right",
data_printer);
fprintf(f, "** Red/Red color violation on right\n");
print_text_helper(RIGHT(root), root, depth, "right",
data_printer, f);
dns_rbt_printtree(DOWN(root), NULL, depth, "down",
data_printer);
print_text_helper(DOWN(root), NULL, depth, "down",
data_printer, f);
} else {
printf("NULL (%s)\n", direction);
fprintf(f, "NULL (%s)\n", direction);
}
}
void
dns_rbt_printall(dns_rbt_t *rbt, void (*data_printer)(FILE *, void *)) {
dns_rbt_printtext(dns_rbt_t *rbt,
void (*data_printer)(FILE *, void *), FILE *f)
{
REQUIRE(VALID_RBT(rbt));
print_text_helper(rbt->root, NULL, 0, "root", data_printer, f);
}
static int
print_dot_helper(dns_rbtnode_t *node, unsigned int *nodecount,
isc_boolean_t show_pointers, FILE *f)
{
unsigned int l, r, d;
if (node == NULL)
return (0);
l = print_dot_helper(LEFT(node), nodecount, show_pointers, f);
r = print_dot_helper(RIGHT(node), nodecount, show_pointers, f);
d = print_dot_helper(DOWN(node), nodecount, show_pointers, f);
*nodecount += 1;
fprintf(f, "node%u[label = \"<f0> |<f1> ", *nodecount);
printnodename(node, ISC_FALSE, f);
fprintf(f, "|<f2>");
if (show_pointers)
fprintf(f, "|<f3> n=%p|<f4> p=%p", node, PARENT(node));
fprintf(f, "\"] [");
if (IS_RED(node))
fprintf(f, "color=red");
else
fprintf(f, "color=black");
/* XXXMUKS: verify that IS_ROOT() indicates subtree root and not
* forest root.
*/
if (IS_ROOT(node))
fprintf(f, ",penwidth=3");
if (IS_EMPTY(node))
fprintf(f, ",style=filled,fillcolor=lightgrey");
fprintf(f, "];\n");
if (LEFT(node) != NULL)
fprintf(f, "\"node%u\":f0 -> \"node%u\":f1;\n", *nodecount, l);
if (DOWN(node) != NULL)
fprintf(f, "\"node%u\":f1 -> \"node%u\":f1 [penwidth=5];\n",
*nodecount, d);
if (RIGHT(node) != NULL)
fprintf(f, "\"node%u\":f2 -> \"node%u\":f1;\n", *nodecount, r);
return (*nodecount);
}
void
dns_rbt_printdot(dns_rbt_t *rbt, isc_boolean_t show_pointers, FILE *f) {
unsigned int nodecount = 0;
REQUIRE(VALID_RBT(rbt));
dns_rbt_printtree(rbt->root, NULL, 0, "root", data_printer);
fprintf(f, "digraph g {\n");
fprintf(f, "node [shape = record,height=.1];\n");
print_dot_helper(rbt->root, &nodecount, show_pointers, f);
fprintf(f, "}\n");
}
/*
@@ -3384,3 +3622,13 @@ dns_rbtnodechain_invalidate(dns_rbtnodechain_t *chain) {
chain->magic = 0;
}
/* XXXMUKS:
*
* - worth removing inline as static functions are inlined automatically
* where suitable by modern compilers.
* - bump the size of dns_rbt.nodecount to size_t.
* - the dumpfile header also contains a nodecount that is unsigned
* int. If large files (> 2^32 nodes) are to be supported, the
* allocation for this field should be increased.
*/

View File

@@ -49,6 +49,7 @@ SRCS = db_test.c \
nsec3_test.c \
private_test.c \
rbt_test.c \
rbt_serialize_test.c \
rdata_test.c \
rdataset_test.c \
rdatasetstats_test.c \
@@ -69,6 +70,7 @@ TARGETS = db_test@EXEEXT@ \
nsec3_test@EXEEXT@ \
private_test@EXEEXT@ \
rbt_test@EXEEXT@ \
rbt_serialize_test@EXEEXT@ \
rdata_test@EXEEXT@ \
rdataset_test@EXEEXT@ \
rdatasetstats_test@EXEEXT@ \
@@ -158,6 +160,11 @@ rbt_test@EXEEXT@: rbt_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
rbt_test.@O@ dnstest.@O@ ${DNSLIBS} \
${ISCLIBS} ${LIBS}
rbt_serialize_test@EXEEXT@: rbt_serialize_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
rbt_serialize_test.@O@ dnstest.@O@ ${DNSLIBS} \
${ISCLIBS} ${LIBS}
rdata_test@EXEEXT@: rdata_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
rdata_test.@O@ ${DNSLIBS} \

View File

@@ -0,0 +1,464 @@
/*
* Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* 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.
*/
/* $Id: rbt_test.c,v 1.1.14.8 2012/02/10 16:24:37 ckb Exp $ */
/* ! \file */
#include <config.h>
#include <atf-c.h>
#include <isc/mem.h>
#include <isc/random.h>
#include <isc/string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#ifdef HAVE_INTTYPES_H
#include <inttypes.h> /* uintptr_t */
#endif
#include <dns/rbt.h>
#include <dns/fixedname.h>
#include <dns/result.h>
#include <dns/compress.h>
#include "dnstest.h"
#include <isc/app.h>
#include <isc/buffer.h>
#include <isc/entropy.h>
#include <isc/file.h>
#include <isc/hash.h>
#include <isc/mem.h>
#include <isc/os.h>
#include <isc/string.h>
#include <isc/socket.h>
#include <isc/stdio.h>
#include <isc/task.h>
#include <isc/timer.h>
#include <isc/util.h>
#include <dns/log.h>
#include <dns/name.h>
#include <dns/result.h>
#include <dst/dst.h>
#ifndef MAP_FILE
#define MAP_FILE 0
#endif
typedef struct data_holder {
int len;
const char *data;
} data_holder_t;
typedef struct rbt_testdata {
const char *name;
size_t name_len;
data_holder_t data;
} rbt_testdata_t;
#define DATA_ITEM(name) { (name), sizeof(name) - 1, { sizeof(name), (name) } }
rbt_testdata_t testdata[] = {
DATA_ITEM("first.com."),
DATA_ITEM("one.net."),
DATA_ITEM("two.com."),
DATA_ITEM("three.org."),
DATA_ITEM("asdf.com."),
DATA_ITEM("ghjkl.com."),
DATA_ITEM("1.edu."),
DATA_ITEM("2.edu."),
DATA_ITEM("3.edu."),
DATA_ITEM("123.edu."),
DATA_ITEM("1236.com."),
DATA_ITEM("and_so_forth.com."),
DATA_ITEM("thisisalongname.com."),
DATA_ITEM("a.b."),
DATA_ITEM("test.net."),
DATA_ITEM("whoknows.org."),
DATA_ITEM("blargh.com."),
DATA_ITEM("www.joe.com."),
DATA_ITEM("test.com."),
DATA_ITEM("isc.org."),
DATA_ITEM("uiop.mil."),
DATA_ITEM("last.fm."),
{ NULL, 0, { 0, NULL } }
};
static void
delete_data(void *data, void *arg) {
UNUSED(arg);
UNUSED(data);
}
static isc_result_t
write_data(FILE *file, unsigned char *datap, void *arg, isc_uint64_t *crc) {
isc_result_t result;
size_t ret = 0;
data_holder_t *data = (data_holder_t *)datap;
data_holder_t temp;
off_t where;
UNUSED(arg);
REQUIRE(file != NULL);
REQUIRE(crc != NULL);
REQUIRE(data != NULL);
REQUIRE((data->len == 0 && data->data == NULL) ||
(data->len != 0 && data->data != NULL));
result = isc_stdio_tell(file, &where);
if (result != ISC_R_SUCCESS)
return (result);
temp = *data;
temp.data = (data->len == 0
? NULL
: (char *)((uintptr_t)where + sizeof(data_holder_t)));
isc_crc64_update(crc, (void *)&temp, sizeof(temp));
ret = fwrite(&temp, sizeof(data_holder_t), 1, file);
if (ret != 1)
return (ISC_R_FAILURE);
if (data->len > 0) {
isc_crc64_update(crc, (const void *)data->data, data->len);
ret = fwrite(data->data, data->len, 1, file);
if (ret != 1)
return (ISC_R_FAILURE);
}
return (ISC_R_SUCCESS);
}
static isc_result_t
fix_data(dns_rbtnode_t *p, void *base, size_t max, void *arg,
isc_uint64_t *crc)
{
data_holder_t *data = p->data;
size_t size;
UNUSED(base);
UNUSED(max);
UNUSED(arg);
REQUIRE(crc != NULL);
REQUIRE(p != NULL);
if (data == NULL)
printf("fixing data: data NULL\n");
else
printf("fixing data: len %d, data %p\n", data->len, data->data);
if (data == NULL ||
(data->len == 0 && data->data != NULL) ||
(data->len != 0 && data->data == NULL))
return (ISC_R_INVALIDFILE);
size = max - ((char *)p - (char *)base);
if (data->len > (int) size || data->data > (const char *) max) {
printf("data invalid\n");
return (ISC_R_INVALIDFILE);
}
isc_crc64_update(crc, (void *)data, sizeof(*data));
data->data = (data->len == 0)
? NULL
: (char *)data + sizeof(data_holder_t);
if (data->len > 0)
isc_crc64_update(crc, (const void *)data->data, data->len);
return (ISC_R_SUCCESS);
}
/*
* Load test data into the RBT.
*/
static void
add_test_data(isc_mem_t *mctx, dns_rbt_t *rbt) {
char buffer[1024];
isc_buffer_t b;
isc_result_t result;
dns_fixedname_t fname;
dns_name_t *name;
dns_compress_t cctx;
rbt_testdata_t *testdatap = testdata;
dns_compress_init(&cctx, -1, mctx);
while (testdatap->name != NULL && testdatap->data.data != NULL) {
memmove(buffer, testdatap->name, testdatap->name_len);
isc_buffer_init(&b, buffer, testdatap->name_len);
isc_buffer_add(&b, testdatap->name_len);
dns_fixedname_init(&fname);
name = dns_fixedname_name(&fname);
result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
if (result != ISC_R_SUCCESS) {
testdatap++;
continue;
}
if (name != NULL) {
result = dns_rbt_addname(rbt, name, &testdatap->data);
ATF_CHECK_STREQ(dns_result_totext(result), "success");
}
testdatap++;
}
dns_compress_invalidate(&cctx);
}
/*
* Walk the tree and ensure that all the test nodes are present.
*/
static void
check_test_data(dns_rbt_t *rbt) {
char buffer[1024];
char *arg;
dns_fixedname_t fname;
dns_fixedname_t fixed;
dns_name_t *name;
isc_buffer_t b;
data_holder_t *data;
isc_result_t result;
dns_name_t *foundname;
rbt_testdata_t *testdatap = testdata;
dns_fixedname_init(&fixed);
foundname = dns_fixedname_name(&fixed);
while (testdatap->name != NULL && testdatap->data.data != NULL) {
memmove(buffer, testdatap->name, testdatap->name_len + 1);
arg = buffer;
isc_buffer_init(&b, arg, testdatap->name_len);
isc_buffer_add(&b, testdatap->name_len);
dns_fixedname_init(&fname);
name = dns_fixedname_name(&fname);
result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
if (result != ISC_R_SUCCESS) {
testdatap++;
continue;
}
data = NULL;
result = dns_rbt_findname(rbt, name, 0, foundname,
(void *) &data);
ATF_CHECK_STREQ(dns_result_totext(result), "success");
testdatap++;
}
}
static void
data_printer(FILE *out, void *datap)
{
data_holder_t *data = (data_holder_t *)datap;
fprintf(out, "%d bytes, %s", data->len, data->data);
}
ATF_TC(serialize);
ATF_TC_HEAD(serialize, tc) {
atf_tc_set_md_var(tc, "descr", "Test writing an rbt to file");
}
ATF_TC_BODY(serialize, tc) {
dns_rbt_t *rbt = NULL;
isc_result_t result;
FILE *rbtfile = NULL;
dns_rbt_t *rbt_deserialized = NULL;
off_t offset;
int fd;
off_t filesize = 0;
char *base;
UNUSED(tc);
isc_mem_debugging = ISC_MEM_DEBUGRECORD;
result = dns_test_begin(NULL, ISC_TRUE);
ATF_CHECK_STREQ(dns_result_totext(result), "success");
result = dns_rbt_create(mctx, delete_data, NULL, &rbt);
ATF_CHECK_STREQ(dns_result_totext(result), "success");
add_test_data(mctx, rbt);
dns_rbt_printtext(rbt, data_printer, stdout);
/*
* Serialize the tree.
*/
printf("serialization begins.\n");
rbtfile = fopen("./zone.bin", "w+b");
ATF_REQUIRE(rbtfile != NULL);
result = dns_rbt_serialize_tree(rbtfile, rbt, write_data, NULL,
&offset);
ATF_REQUIRE(result == ISC_R_SUCCESS);
dns_rbt_destroy(&rbt);
/*
* Deserialize the tree
*/
printf("deserialization begins.\n");
/*
* Map in the whole file in one go
*/
fd = open("zone.bin", O_RDWR);
isc_file_getsizefd(fd, &filesize);
base = mmap(NULL, filesize,
PROT_READ|PROT_WRITE,
MAP_FILE|MAP_PRIVATE, fd, 0);
ATF_REQUIRE(base != NULL && base != MAP_FAILED);
close(fd);
result = dns_rbt_deserialize_tree(base, filesize, 0, mctx,
delete_data, NULL, fix_data, NULL,
NULL, &rbt_deserialized);
/* Test to make sure we have a valid tree */
ATF_REQUIRE(result == ISC_R_SUCCESS);
if (rbt_deserialized == NULL)
atf_tc_fail("deserialized rbt is null!"); /* Abort execution. */
check_test_data(rbt_deserialized);
dns_rbt_printtext(rbt_deserialized, data_printer, stdout);
dns_rbt_destroy(&rbt_deserialized);
munmap(base, filesize);
unlink("zone.bin");
dns_test_end();
}
ATF_TC(deserialize_corrupt);
ATF_TC_HEAD(deserialize_corrupt, tc) {
atf_tc_set_md_var(tc, "descr", "Test reading a corrupt map file");
}
ATF_TC_BODY(deserialize_corrupt, tc) {
dns_rbt_t *rbt = NULL;
isc_result_t result;
FILE *rbtfile = NULL;
off_t offset;
int fd;
off_t filesize = 0;
char *base, *p, *q;
isc_uint32_t r;
int i;
UNUSED(tc);
isc_mem_debugging = ISC_MEM_DEBUGRECORD;
result = dns_test_begin(NULL, ISC_TRUE);
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
/* Set up map file */
result = dns_rbt_create(mctx, delete_data, NULL, &rbt);
ATF_CHECK_EQ(result, ISC_R_SUCCESS);
add_test_data(mctx, rbt);
rbtfile = fopen("./zone.bin", "w+b");
ATF_REQUIRE(rbtfile != NULL);
result = dns_rbt_serialize_tree(rbtfile, rbt, write_data, NULL,
&offset);
ATF_REQUIRE(result == ISC_R_SUCCESS);
dns_rbt_destroy(&rbt);
/* Read back with random fuzzing */
for (i = 0; i < 256; i++) {
dns_rbt_t *rbt_deserialized = NULL;
fd = open("zone.bin", O_RDWR);
isc_file_getsizefd(fd, &filesize);
base = mmap(NULL, filesize,
PROT_READ|PROT_WRITE,
MAP_FILE|MAP_PRIVATE, fd, 0);
ATF_REQUIRE(base != NULL && base != MAP_FAILED);
close(fd);
/* Randomly fuzz a portion of the memory */
isc_random_get(&r);
p = base + (r % filesize);
q = base + filesize;
isc_random_get(&r);
q -= (r % (q - p));
while (p++ < q) {
isc_random_get(&r);
*p = r & 0xff;
}
result = dns_rbt_deserialize_tree(base, filesize, 0, mctx,
delete_data, NULL,
fix_data, NULL,
NULL, &rbt_deserialized);
printf("%d: %s\n", i, isc_result_totext(result));
/* Test to make sure we have a valid tree */
ATF_REQUIRE(result == ISC_R_SUCCESS ||
result == ISC_R_INVALIDFILE);
if (result != ISC_R_SUCCESS)
ATF_REQUIRE(rbt_deserialized == NULL);
if (rbt_deserialized != NULL)
dns_rbt_destroy(&rbt_deserialized);
munmap(base, filesize);
}
unlink("zone.bin");
dns_test_end();
}
ATF_TC(serialize_align);
ATF_TC_HEAD(serialize_align, tc) {
atf_tc_set_md_var(tc, "descr",
"Test the dns_rbt_serialize_align() function.");
}
ATF_TC_BODY(serialize_align, tc) {
UNUSED(tc);
ATF_CHECK(dns_rbt_serialize_align(0) == 0);
ATF_CHECK(dns_rbt_serialize_align(1) == 8);
ATF_CHECK(dns_rbt_serialize_align(2) == 8);
ATF_CHECK(dns_rbt_serialize_align(3) == 8);
ATF_CHECK(dns_rbt_serialize_align(4) == 8);
ATF_CHECK(dns_rbt_serialize_align(5) == 8);
ATF_CHECK(dns_rbt_serialize_align(6) == 8);
ATF_CHECK(dns_rbt_serialize_align(7) == 8);
ATF_CHECK(dns_rbt_serialize_align(8) == 8);
ATF_CHECK(dns_rbt_serialize_align(9) == 16);
ATF_CHECK(dns_rbt_serialize_align(0xff) == 0x100);
ATF_CHECK(dns_rbt_serialize_align(0x301) == 0x308);
}
/*
* Main
*/
ATF_TP_ADD_TCS(tp) {
ATF_TP_ADD_TC(tp, serialize);
ATF_TP_ADD_TC(tp, deserialize_corrupt);
ATF_TP_ADD_TC(tp, serialize_align);
return (atf_no_error());
}

File diff suppressed because it is too large Load Diff

View File

@@ -553,7 +553,8 @@ dns_rbt_fullnamefromnode
dns_rbt_hashsize
dns_rbt_namefromnode
dns_rbt_nodecount
dns_rbt_printall
dns_rbt_printtext
dns_rbt_printdot
dns_rbt_printnodeinfo
dns_rbt_serialize_align
dns_rbt_serialize_tree

View File

@@ -393,3 +393,17 @@ isc_hash_calc(const unsigned char *key, unsigned int keylen,
return (hash_calc(hash, key, keylen, case_sensitive));
}
void
isc__hash_setvec(const uint16_t *vec) {
int i;
hash_random_t *p;
if (hash == NULL)
return;
p = hash->rndvector;
for (i = 0; i < 256; i++) {
p[i] = vec[i];
}
}

View File

@@ -20,6 +20,8 @@
#ifndef ISC_HASH_H
#define ISC_HASH_H 1
#include <stdint.h>
/*****
***** Module Info
*****/
@@ -180,6 +182,22 @@ isc_hash_calc(const unsigned char *key, unsigned int keylen,
*/
/*@}*/
void
isc__hash_setvec(const uint16_t *vec);
/*!<
* \brief Set the contents of the random vector used in hashing.
*
* WARNING: This function is meant to be used only in testing code. It
* must not be used anywhere in normally running code.
*
* The hash context must have been created beforehand, otherwise this
* function is a nop.
*
* 'vec' is not documented here on purpose. You should know what you are
* doing before using this function.
*/
ISC_LANG_ENDDECLS
#endif /* ISC_HASH_H */