mirror of
https://github.com/monero-project/monero.git
synced 2025-12-10 13:00:22 -05:00
added unbound to external deps
This commit is contained in:
parent
732493c5cb
commit
9ef094b356
394 changed files with 199264 additions and 0 deletions
523
external/unbound/testcode/asynclook.c
vendored
Normal file
523
external/unbound/testcode/asynclook.c
vendored
Normal file
|
|
@ -0,0 +1,523 @@
|
|||
/*
|
||||
* testcode/asynclook.c - debug program perform async libunbound queries.
|
||||
*
|
||||
* Copyright (c) 2008, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This program shows the results from several background lookups,
|
||||
* while printing time in the foreground.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
#include "libunbound/unbound.h"
|
||||
#include "libunbound/context.h"
|
||||
#include "util/locks.h"
|
||||
#include "util/log.h"
|
||||
#include "ldns/rrdef.h"
|
||||
#ifdef UNBOUND_ALLOC_LITE
|
||||
#undef malloc
|
||||
#undef calloc
|
||||
#undef realloc
|
||||
#undef free
|
||||
#undef strdup
|
||||
#endif
|
||||
|
||||
/** keeping track of the async ids */
|
||||
struct track_id {
|
||||
/** the id to pass to libunbound to cancel */
|
||||
int id;
|
||||
/** true if cancelled */
|
||||
int cancel;
|
||||
/** a lock on this structure for thread safety */
|
||||
lock_basic_t lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* result list for the lookups
|
||||
*/
|
||||
struct lookinfo {
|
||||
/** name to look up */
|
||||
char* name;
|
||||
/** tracking number that can be used to cancel the query */
|
||||
int async_id;
|
||||
/** error code from libunbound */
|
||||
int err;
|
||||
/** result from lookup */
|
||||
struct ub_result* result;
|
||||
};
|
||||
|
||||
/** global variable to see how many queries we have left */
|
||||
static int num_wait = 0;
|
||||
|
||||
/** usage information for asynclook */
|
||||
static void usage(char* argv[])
|
||||
{
|
||||
printf("usage: %s [options] name ...\n", argv[0]);
|
||||
printf("names are looked up at the same time, asynchronously.\n");
|
||||
printf(" -b : use blocking requests\n");
|
||||
printf(" -c : cancel the requests\n");
|
||||
printf(" -d : enable debug output\n");
|
||||
printf(" -f addr : use addr, forward to that server\n");
|
||||
printf(" -h : this help message\n");
|
||||
printf(" -H fname : read hosts from fname\n");
|
||||
printf(" -r fname : read resolv.conf from fname\n");
|
||||
printf(" -t : use a resolver thread instead of forking a process\n");
|
||||
printf(" -x : perform extended threaded test\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** print result from lookup nicely */
|
||||
static void
|
||||
print_result(struct lookinfo* info)
|
||||
{
|
||||
char buf[100];
|
||||
if(info->err) /* error (from libunbound) */
|
||||
printf("%s: error %s\n", info->name,
|
||||
ub_strerror(info->err));
|
||||
else if(!info->result)
|
||||
printf("%s: cancelled\n", info->name);
|
||||
else if(info->result->havedata)
|
||||
printf("%s: %s\n", info->name,
|
||||
inet_ntop(AF_INET, info->result->data[0],
|
||||
buf, (socklen_t)sizeof(buf)));
|
||||
else {
|
||||
/* there is no data, why that? */
|
||||
if(info->result->rcode == 0 /*noerror*/ ||
|
||||
info->result->nxdomain)
|
||||
printf("%s: no data %s\n", info->name,
|
||||
info->result->nxdomain?"(no such host)":
|
||||
"(no IP4 address)");
|
||||
else /* some error (from the server) */
|
||||
printf("%s: DNS error %d\n", info->name,
|
||||
info->result->rcode);
|
||||
}
|
||||
}
|
||||
|
||||
/** this is a function of type ub_callback_t */
|
||||
static void
|
||||
lookup_is_done(void* mydata, int err, struct ub_result* result)
|
||||
{
|
||||
/* cast mydata back to the correct type */
|
||||
struct lookinfo* info = (struct lookinfo*)mydata;
|
||||
fprintf(stderr, "name %s resolved\n", info->name);
|
||||
info->err = err;
|
||||
info->result = result;
|
||||
/* one less to wait for */
|
||||
num_wait--;
|
||||
}
|
||||
|
||||
/** check error, if bad, exit with error message */
|
||||
static void
|
||||
checkerr(const char* desc, int err)
|
||||
{
|
||||
if(err != 0) {
|
||||
printf("%s error: %s\n", desc, ub_strerror(err));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef THREADS_DISABLED
|
||||
/** only one process can communicate with async worker */
|
||||
#define NUMTHR 1
|
||||
#else /* have threads */
|
||||
/** number of threads to make in extended test */
|
||||
#define NUMTHR 10
|
||||
#endif
|
||||
|
||||
/** struct for extended thread info */
|
||||
struct ext_thr_info {
|
||||
/** thread num for debug */
|
||||
int thread_num;
|
||||
/** thread id */
|
||||
ub_thread_t tid;
|
||||
/** context */
|
||||
struct ub_ctx* ctx;
|
||||
/** size of array to query */
|
||||
int argc;
|
||||
/** array of names to query */
|
||||
char** argv;
|
||||
/** number of queries to do */
|
||||
int numq;
|
||||
};
|
||||
|
||||
/** if true, we are testing against 'localhost' and extra checking is done */
|
||||
static int q_is_localhost = 0;
|
||||
|
||||
/** check result structure for the 'correct' answer */
|
||||
static void
|
||||
ext_check_result(const char* desc, int err, struct ub_result* result)
|
||||
{
|
||||
checkerr(desc, err);
|
||||
if(result == NULL) {
|
||||
printf("%s: error result is NULL.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(q_is_localhost) {
|
||||
if(strcmp(result->qname, "localhost") != 0) {
|
||||
printf("%s: error result has wrong qname.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->qtype != LDNS_RR_TYPE_A) {
|
||||
printf("%s: error result has wrong qtype.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->qclass != LDNS_RR_CLASS_IN) {
|
||||
printf("%s: error result has wrong qclass.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->data == NULL) {
|
||||
printf("%s: error result->data is NULL.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->len == NULL) {
|
||||
printf("%s: error result->len is NULL.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->rcode != 0) {
|
||||
printf("%s: error result->rcode is set.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->havedata == 0) {
|
||||
printf("%s: error result->havedata is unset.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->nxdomain != 0) {
|
||||
printf("%s: error result->nxdomain is set.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->secure || result->bogus) {
|
||||
printf("%s: error result->secure or bogus is set.\n",
|
||||
desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->data[0] == NULL) {
|
||||
printf("%s: error result->data[0] is NULL.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->len[0] != 4) {
|
||||
printf("%s: error result->len[0] is wrong.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->len[1] != 0 || result->data[1] != NULL) {
|
||||
printf("%s: error result->data[1] or len[1] is "
|
||||
"wrong.\n", desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->answer_packet == NULL) {
|
||||
printf("%s: error result->answer_packet is NULL.\n",
|
||||
desc);
|
||||
exit(1);
|
||||
}
|
||||
if(result->answer_len != 54) {
|
||||
printf("%s: error result->answer_len is wrong.\n",
|
||||
desc);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** extended bg result callback, this function is ub_callback_t */
|
||||
static void
|
||||
ext_callback(void* mydata, int err, struct ub_result* result)
|
||||
{
|
||||
struct track_id* my_id = (struct track_id*)mydata;
|
||||
int doprint = 0;
|
||||
if(my_id) {
|
||||
/* I have an id, make sure we are not cancelled */
|
||||
lock_basic_lock(&my_id->lock);
|
||||
if(doprint)
|
||||
printf("cb %d: ", my_id->id);
|
||||
if(my_id->cancel) {
|
||||
printf("error: query id=%d returned, but was cancelled\n",
|
||||
my_id->id);
|
||||
abort();
|
||||
exit(1);
|
||||
}
|
||||
lock_basic_unlock(&my_id->lock);
|
||||
}
|
||||
ext_check_result("ext_callback", err, result);
|
||||
log_assert(result);
|
||||
if(doprint) {
|
||||
struct lookinfo pi;
|
||||
pi.name = result?result->qname:"noname";
|
||||
pi.result = result;
|
||||
pi.err = 0;
|
||||
print_result(&pi);
|
||||
}
|
||||
ub_resolve_free(result);
|
||||
}
|
||||
|
||||
/** extended thread worker */
|
||||
static void*
|
||||
ext_thread(void* arg)
|
||||
{
|
||||
struct ext_thr_info* inf = (struct ext_thr_info*)arg;
|
||||
int i, r;
|
||||
struct ub_result* result;
|
||||
struct track_id* async_ids = NULL;
|
||||
log_thread_set(&inf->thread_num);
|
||||
if(inf->thread_num > NUMTHR*2/3) {
|
||||
async_ids = (struct track_id*)calloc((size_t)inf->numq, sizeof(struct track_id));
|
||||
if(!async_ids) {
|
||||
printf("out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
for(i=0; i<inf->numq; i++) {
|
||||
lock_basic_init(&async_ids[i].lock);
|
||||
}
|
||||
}
|
||||
for(i=0; i<inf->numq; i++) {
|
||||
if(async_ids) {
|
||||
r = ub_resolve_async(inf->ctx,
|
||||
inf->argv[i%inf->argc], LDNS_RR_TYPE_A,
|
||||
LDNS_RR_CLASS_IN, &async_ids[i], ext_callback,
|
||||
&async_ids[i].id);
|
||||
checkerr("ub_resolve_async", r);
|
||||
if(i > 100) {
|
||||
lock_basic_lock(&async_ids[i-100].lock);
|
||||
r = ub_cancel(inf->ctx, async_ids[i-100].id);
|
||||
if(r != UB_NOID)
|
||||
async_ids[i-100].cancel=1;
|
||||
lock_basic_unlock(&async_ids[i-100].lock);
|
||||
if(r != UB_NOID)
|
||||
checkerr("ub_cancel", r);
|
||||
}
|
||||
} else if(inf->thread_num > NUMTHR/2) {
|
||||
/* async */
|
||||
r = ub_resolve_async(inf->ctx,
|
||||
inf->argv[i%inf->argc], LDNS_RR_TYPE_A,
|
||||
LDNS_RR_CLASS_IN, NULL, ext_callback, NULL);
|
||||
checkerr("ub_resolve_async", r);
|
||||
} else {
|
||||
/* blocking */
|
||||
r = ub_resolve(inf->ctx, inf->argv[i%inf->argc],
|
||||
LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN, &result);
|
||||
ext_check_result("ub_resolve", r, result);
|
||||
ub_resolve_free(result);
|
||||
}
|
||||
}
|
||||
if(inf->thread_num > NUMTHR/2) {
|
||||
r = ub_wait(inf->ctx);
|
||||
checkerr("ub_ctx_wait", r);
|
||||
}
|
||||
if(async_ids) {
|
||||
for(i=0; i<inf->numq; i++) {
|
||||
lock_basic_destroy(&async_ids[i].lock);
|
||||
}
|
||||
}
|
||||
free(async_ids);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** perform extended threaded test */
|
||||
static int
|
||||
ext_test(struct ub_ctx* ctx, int argc, char** argv)
|
||||
{
|
||||
struct ext_thr_info inf[NUMTHR];
|
||||
int i;
|
||||
if(argc == 1 && strcmp(argv[0], "localhost") == 0)
|
||||
q_is_localhost = 1;
|
||||
printf("extended test start (%d threads)\n", NUMTHR);
|
||||
for(i=0; i<NUMTHR; i++) {
|
||||
/* 0 = this, 1 = library bg worker */
|
||||
inf[i].thread_num = i+2;
|
||||
inf[i].ctx = ctx;
|
||||
inf[i].argc = argc;
|
||||
inf[i].argv = argv;
|
||||
inf[i].numq = 100;
|
||||
ub_thread_create(&inf[i].tid, ext_thread, &inf[i]);
|
||||
}
|
||||
/* the work happens here */
|
||||
for(i=0; i<NUMTHR; i++) {
|
||||
ub_thread_join(inf[i].tid);
|
||||
}
|
||||
printf("extended test end\n");
|
||||
ub_ctx_delete(ctx);
|
||||
checklock_stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern int optind;
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern char* optarg;
|
||||
|
||||
/** main program for asynclook */
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int c;
|
||||
struct ub_ctx* ctx;
|
||||
struct lookinfo* lookups;
|
||||
int i, r, cancel=0, blocking=0, ext=0;
|
||||
|
||||
/* init log now because solaris thr_key_create() is not threadsafe */
|
||||
log_init(0,0,0);
|
||||
/* lock debug start (if any) */
|
||||
checklock_start();
|
||||
|
||||
/* create context */
|
||||
ctx = ub_ctx_create();
|
||||
if(!ctx) {
|
||||
printf("could not create context, %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* command line options */
|
||||
if(argc == 1) {
|
||||
usage(argv);
|
||||
}
|
||||
while( (c=getopt(argc, argv, "bcdf:hH:r:tx")) != -1) {
|
||||
switch(c) {
|
||||
case 'd':
|
||||
r = ub_ctx_debuglevel(ctx, 3);
|
||||
checkerr("ub_ctx_debuglevel", r);
|
||||
break;
|
||||
case 't':
|
||||
r = ub_ctx_async(ctx, 1);
|
||||
checkerr("ub_ctx_async", r);
|
||||
break;
|
||||
case 'c':
|
||||
cancel = 1;
|
||||
break;
|
||||
case 'b':
|
||||
blocking = 1;
|
||||
break;
|
||||
case 'r':
|
||||
r = ub_ctx_resolvconf(ctx, optarg);
|
||||
if(r != 0) {
|
||||
printf("ub_ctx_resolvconf "
|
||||
"error: %s : %s\n",
|
||||
ub_strerror(r),
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
r = ub_ctx_hosts(ctx, optarg);
|
||||
if(r != 0) {
|
||||
printf("ub_ctx_hosts "
|
||||
"error: %s : %s\n",
|
||||
ub_strerror(r),
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
r = ub_ctx_set_fwd(ctx, optarg);
|
||||
checkerr("ub_ctx_set_fwd", r);
|
||||
break;
|
||||
case 'x':
|
||||
ext = 1;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
default:
|
||||
usage(argv);
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if(ext)
|
||||
return ext_test(ctx, argc, argv);
|
||||
|
||||
/* allocate array for results. */
|
||||
lookups = (struct lookinfo*)calloc((size_t)argc,
|
||||
sizeof(struct lookinfo));
|
||||
if(!lookups) {
|
||||
printf("out of memory\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* perform asyncronous calls */
|
||||
num_wait = argc;
|
||||
for(i=0; i<argc; i++) {
|
||||
lookups[i].name = argv[i];
|
||||
if(blocking) {
|
||||
fprintf(stderr, "lookup %s\n", argv[i]);
|
||||
r = ub_resolve(ctx, argv[i], LDNS_RR_TYPE_A,
|
||||
LDNS_RR_CLASS_IN, &lookups[i].result);
|
||||
checkerr("ub_resolve", r);
|
||||
} else {
|
||||
fprintf(stderr, "start async lookup %s\n", argv[i]);
|
||||
r = ub_resolve_async(ctx, argv[i], LDNS_RR_TYPE_A,
|
||||
LDNS_RR_CLASS_IN, &lookups[i], &lookup_is_done,
|
||||
&lookups[i].async_id);
|
||||
checkerr("ub_resolve_async", r);
|
||||
}
|
||||
}
|
||||
if(blocking)
|
||||
num_wait = 0;
|
||||
else if(cancel) {
|
||||
for(i=0; i<argc; i++) {
|
||||
fprintf(stderr, "cancel %s\n", argv[i]);
|
||||
r = ub_cancel(ctx, lookups[i].async_id);
|
||||
if(r != UB_NOID)
|
||||
checkerr("ub_cancel", r);
|
||||
}
|
||||
num_wait = 0;
|
||||
}
|
||||
|
||||
/* wait while the hostnames are looked up. Do something useful here */
|
||||
if(num_wait > 0)
|
||||
for(i=0; i<1000; i++) {
|
||||
usleep(100000);
|
||||
fprintf(stderr, "%g seconds passed\n", 0.1*(double)i);
|
||||
r = ub_process(ctx);
|
||||
checkerr("ub_process", r);
|
||||
if(num_wait == 0)
|
||||
break;
|
||||
}
|
||||
if(i>=999) {
|
||||
printf("timed out\n");
|
||||
return 0;
|
||||
}
|
||||
printf("lookup complete\n");
|
||||
|
||||
/* print lookup results */
|
||||
for(i=0; i<argc; i++) {
|
||||
print_result(&lookups[i]);
|
||||
ub_resolve_free(lookups[i].result);
|
||||
}
|
||||
|
||||
ub_ctx_delete(ctx);
|
||||
free(lookups);
|
||||
checklock_stop();
|
||||
return 0;
|
||||
}
|
||||
848
external/unbound/testcode/checklocks.c
vendored
Normal file
848
external/unbound/testcode/checklocks.c
vendored
Normal file
|
|
@ -0,0 +1,848 @@
|
|||
/**
|
||||
* testcode/checklocks.c - wrapper on locks that checks access.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <signal.h>
|
||||
#include "util/locks.h" /* include before checklocks.h */
|
||||
#include "testcode/checklocks.h"
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Locks that are checked.
|
||||
*
|
||||
* Ugly hack: uses the fact that workers start with an int thread_num, and
|
||||
* are passed to thread_create to make the thread numbers here the same as
|
||||
* those used for logging which is nice.
|
||||
*
|
||||
* Todo:
|
||||
* - debug status print, of thread lock stacks, and current waiting.
|
||||
*/
|
||||
#ifdef USE_THREAD_DEBUG
|
||||
|
||||
/** How long to wait before lock attempt is a failure. */
|
||||
#define CHECK_LOCK_TIMEOUT 120 /* seconds */
|
||||
/** How long to wait before join attempt is a failure. */
|
||||
#define CHECK_JOIN_TIMEOUT 120 /* seconds */
|
||||
|
||||
/** if key has been created */
|
||||
static int key_created = 0;
|
||||
/** if the key was deleted, i.e. we have quit */
|
||||
static int key_deleted = 0;
|
||||
/** we hide the thread debug info with this key. */
|
||||
static ub_thread_key_t thr_debug_key;
|
||||
/** the list of threads, so all threads can be examined. NULL if unused. */
|
||||
static struct thr_check* thread_infos[THRDEBUG_MAX_THREADS];
|
||||
/** do we check locking order */
|
||||
int check_locking_order = 1;
|
||||
/** the pid of this runset, reasonably unique. */
|
||||
static pid_t check_lock_pid;
|
||||
|
||||
/** print all possible debug info on the state of the system */
|
||||
static void total_debug_info(void);
|
||||
|
||||
/** print pretty lock error and exit */
|
||||
static void lock_error(struct checked_lock* lock,
|
||||
const char* func, const char* file, int line, const char* err)
|
||||
{
|
||||
log_err("lock error (description follows)");
|
||||
log_err("Created at %s %s:%d", lock->create_func,
|
||||
lock->create_file, lock->create_line);
|
||||
if(lock->holder_func && lock->holder_file)
|
||||
log_err("Previously %s %s:%d", lock->holder_func,
|
||||
lock->holder_file, lock->holder_line);
|
||||
log_err("At %s %s:%d", func, file, line);
|
||||
log_err("Error for %s lock: %s",
|
||||
(lock->type==check_lock_mutex)?"mutex": (
|
||||
(lock->type==check_lock_spinlock)?"spinlock": (
|
||||
(lock->type==check_lock_rwlock)?"rwlock": "badtype")), err);
|
||||
log_err("complete status display:");
|
||||
total_debug_info();
|
||||
fatal_exit("bailing out");
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain lock on debug lock structure. This could be a deadlock by the caller.
|
||||
* The debug code itself does not deadlock. Anyway, check with timeouts.
|
||||
* @param lock: on what to acquire lock.
|
||||
* @param func: user level caller identification.
|
||||
* @param file: user level caller identification.
|
||||
* @param line: user level caller identification.
|
||||
*/
|
||||
static void
|
||||
acquire_locklock(struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
struct timespec to;
|
||||
int err;
|
||||
int contend = 0;
|
||||
/* first try; inc contention counter if not immediately */
|
||||
if((err = pthread_mutex_trylock(&lock->lock))) {
|
||||
if(err==EBUSY)
|
||||
contend++;
|
||||
else fatal_exit("error in mutex_trylock: %s", strerror(err));
|
||||
}
|
||||
if(!err)
|
||||
return; /* immediate success */
|
||||
to.tv_sec = time(NULL) + CHECK_LOCK_TIMEOUT;
|
||||
to.tv_nsec = 0;
|
||||
err = pthread_mutex_timedlock(&lock->lock, &to);
|
||||
if(err) {
|
||||
log_err("in acquiring locklock: %s", strerror(err));
|
||||
lock_error(lock, func, file, line, "acquire locklock");
|
||||
}
|
||||
/* since we hold the lock, we can edit the contention_count */
|
||||
lock->contention_count += contend;
|
||||
}
|
||||
|
||||
/** add protected region */
|
||||
void
|
||||
lock_protect(void *p, void* area, size_t size)
|
||||
{
|
||||
struct checked_lock* lock = *(struct checked_lock**)p;
|
||||
struct protected_area* e = (struct protected_area*)malloc(
|
||||
sizeof(struct protected_area));
|
||||
if(!e)
|
||||
fatal_exit("lock_protect: out of memory");
|
||||
e->region = area;
|
||||
e->size = size;
|
||||
e->hold = malloc(size);
|
||||
if(!e->hold)
|
||||
fatal_exit("lock_protect: out of memory");
|
||||
memcpy(e->hold, e->region, e->size);
|
||||
|
||||
acquire_locklock(lock, __func__, __FILE__, __LINE__);
|
||||
e->next = lock->prot;
|
||||
lock->prot = e;
|
||||
LOCKRET(pthread_mutex_unlock(&lock->lock));
|
||||
}
|
||||
|
||||
/** remove protected region */
|
||||
void
|
||||
lock_unprotect(void* mangled, void* area)
|
||||
{
|
||||
struct checked_lock* lock = *(struct checked_lock**)mangled;
|
||||
struct protected_area* p, **prevp;
|
||||
if(!lock)
|
||||
return;
|
||||
acquire_locklock(lock, __func__, __FILE__, __LINE__);
|
||||
p = lock->prot;
|
||||
prevp = &lock->prot;
|
||||
while(p) {
|
||||
if(p->region == area) {
|
||||
*prevp = p->next;
|
||||
free(p->hold);
|
||||
free(p);
|
||||
LOCKRET(pthread_mutex_unlock(&lock->lock));
|
||||
return;
|
||||
}
|
||||
prevp = &p->next;
|
||||
p = p->next;
|
||||
}
|
||||
LOCKRET(pthread_mutex_unlock(&lock->lock));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check protected memory region. Memory compare. Exit on error.
|
||||
* @param lock: which lock to check.
|
||||
* @param func: location we are now (when failure is detected).
|
||||
* @param file: location we are now (when failure is detected).
|
||||
* @param line: location we are now (when failure is detected).
|
||||
*/
|
||||
static void
|
||||
prot_check(struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
struct protected_area* p = lock->prot;
|
||||
while(p) {
|
||||
if(memcmp(p->hold, p->region, p->size) != 0) {
|
||||
log_hex("memory prev", p->hold, p->size);
|
||||
log_hex("memory here", p->region, p->size);
|
||||
lock_error(lock, func, file, line,
|
||||
"protected area modified");
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
/** Copy protected memory region */
|
||||
static void
|
||||
prot_store(struct checked_lock* lock)
|
||||
{
|
||||
struct protected_area* p = lock->prot;
|
||||
while(p) {
|
||||
memcpy(p->hold, p->region, p->size);
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
/** get memory held by lock */
|
||||
size_t
|
||||
lock_get_mem(void* pp)
|
||||
{
|
||||
size_t s;
|
||||
struct checked_lock* lock = *(struct checked_lock**)pp;
|
||||
struct protected_area* p;
|
||||
s = sizeof(struct checked_lock);
|
||||
acquire_locklock(lock, __func__, __FILE__, __LINE__);
|
||||
for(p = lock->prot; p; p = p->next) {
|
||||
s += sizeof(struct protected_area);
|
||||
s += p->size;
|
||||
}
|
||||
LOCKRET(pthread_mutex_unlock(&lock->lock));
|
||||
return s;
|
||||
}
|
||||
|
||||
/** write lock trace info to file, while you hold those locks */
|
||||
static void
|
||||
ordercheck_locklock(struct thr_check* thr, struct checked_lock* lock)
|
||||
{
|
||||
int info[4];
|
||||
if(!check_locking_order) return;
|
||||
if(!thr->holding_first) return; /* no older lock, no info */
|
||||
/* write: <lock id held> <lock id new> <file> <line> */
|
||||
info[0] = thr->holding_first->create_thread;
|
||||
info[1] = thr->holding_first->create_instance;
|
||||
info[2] = lock->create_thread;
|
||||
info[3] = lock->create_instance;
|
||||
if(fwrite(info, 4*sizeof(int), 1, thr->order_info) != 1 ||
|
||||
fwrite(lock->holder_file, strlen(lock->holder_file)+1, 1,
|
||||
thr->order_info) != 1 ||
|
||||
fwrite(&lock->holder_line, sizeof(int), 1,
|
||||
thr->order_info) != 1)
|
||||
log_err("fwrite: %s", strerror(errno));
|
||||
}
|
||||
|
||||
/** write ordercheck lock creation details to file */
|
||||
static void
|
||||
ordercheck_lockcreate(struct thr_check* thr, struct checked_lock* lock)
|
||||
{
|
||||
/* write: <ffff = create> <lock id> <file> <line> */
|
||||
int cmd = -1;
|
||||
if(!check_locking_order) return;
|
||||
|
||||
if( fwrite(&cmd, sizeof(int), 1, thr->order_info) != 1 ||
|
||||
fwrite(&lock->create_thread, sizeof(int), 1,
|
||||
thr->order_info) != 1 ||
|
||||
fwrite(&lock->create_instance, sizeof(int), 1,
|
||||
thr->order_info) != 1 ||
|
||||
fwrite(lock->create_file, strlen(lock->create_file)+1, 1,
|
||||
thr->order_info) != 1 ||
|
||||
fwrite(&lock->create_line, sizeof(int), 1,
|
||||
thr->order_info) != 1)
|
||||
log_err("fwrite: %s", strerror(errno));
|
||||
}
|
||||
|
||||
/** alloc struct, init lock empty */
|
||||
void
|
||||
checklock_init(enum check_lock_type type, struct checked_lock** lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
struct checked_lock* e = (struct checked_lock*)calloc(1,
|
||||
sizeof(struct checked_lock));
|
||||
struct thr_check *thr = (struct thr_check*)pthread_getspecific(
|
||||
thr_debug_key);
|
||||
if(!e)
|
||||
fatal_exit("%s %s %d: out of memory", func, file, line);
|
||||
if(!thr) {
|
||||
/* this is called when log_init() calls lock_init()
|
||||
* functions, and the test check code has not yet
|
||||
* been initialised. But luckily, the checklock_start()
|
||||
* routine can be called multiple times without ill effect.
|
||||
*/
|
||||
checklock_start();
|
||||
thr = (struct thr_check*)pthread_getspecific(thr_debug_key);
|
||||
}
|
||||
if(!thr)
|
||||
fatal_exit("%s %s %d: lock_init no thread info", func, file,
|
||||
line);
|
||||
*lock = e;
|
||||
e->type = type;
|
||||
e->create_func = func;
|
||||
e->create_file = file;
|
||||
e->create_line = line;
|
||||
e->create_thread = thr->num;
|
||||
e->create_instance = thr->locks_created++;
|
||||
ordercheck_lockcreate(thr, e);
|
||||
LOCKRET(pthread_mutex_init(&e->lock, NULL));
|
||||
switch(e->type) {
|
||||
case check_lock_mutex:
|
||||
LOCKRET(pthread_mutex_init(&e->u.mutex, NULL));
|
||||
break;
|
||||
case check_lock_spinlock:
|
||||
LOCKRET(pthread_spin_init(&e->u.spinlock, PTHREAD_PROCESS_PRIVATE));
|
||||
break;
|
||||
case check_lock_rwlock:
|
||||
LOCKRET(pthread_rwlock_init(&e->u.rwlock, NULL));
|
||||
break;
|
||||
default:
|
||||
log_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
/** delete prot items */
|
||||
static void
|
||||
prot_clear(struct checked_lock* lock)
|
||||
{
|
||||
struct protected_area* p=lock->prot, *np;
|
||||
while(p) {
|
||||
np = p->next;
|
||||
free(p->hold);
|
||||
free(p);
|
||||
p = np;
|
||||
}
|
||||
}
|
||||
|
||||
/** check if type is OK for the lock given */
|
||||
static void
|
||||
checktype(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
if(!lock)
|
||||
fatal_exit("use of null/deleted lock at %s %s:%d",
|
||||
func, file, line);
|
||||
if(type != lock->type) {
|
||||
lock_error(lock, func, file, line, "wrong lock type");
|
||||
}
|
||||
}
|
||||
|
||||
/** check if OK, free struct */
|
||||
void
|
||||
checklock_destroy(enum check_lock_type type, struct checked_lock** lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
const size_t contention_interest = 1; /* promille contented locks */
|
||||
struct checked_lock* e;
|
||||
if(!lock)
|
||||
return;
|
||||
e = *lock;
|
||||
if(!e)
|
||||
return;
|
||||
checktype(type, e, func, file, line);
|
||||
|
||||
/* check if delete is OK */
|
||||
acquire_locklock(e, func, file, line);
|
||||
if(e->hold_count != 0)
|
||||
lock_error(e, func, file, line, "delete while locked.");
|
||||
if(e->wait_count != 0)
|
||||
lock_error(e, func, file, line, "delete while waited on.");
|
||||
prot_check(e, func, file, line);
|
||||
*lock = NULL; /* use after free will fail */
|
||||
LOCKRET(pthread_mutex_unlock(&e->lock));
|
||||
|
||||
/* contention, look at fraction in trouble. */
|
||||
if(e->history_count > 1 &&
|
||||
1000*e->contention_count/e->history_count > contention_interest) {
|
||||
log_info("lock created %s %s %d has contention %u of %u (%d%%)",
|
||||
e->create_func, e->create_file, e->create_line,
|
||||
(unsigned int)e->contention_count,
|
||||
(unsigned int)e->history_count,
|
||||
(int)(100*e->contention_count/e->history_count));
|
||||
}
|
||||
|
||||
/* delete it */
|
||||
LOCKRET(pthread_mutex_destroy(&e->lock));
|
||||
prot_clear(e);
|
||||
/* since nobody holds the lock - see check above, no need to unlink
|
||||
* from the thread-held locks list. */
|
||||
switch(e->type) {
|
||||
case check_lock_mutex:
|
||||
LOCKRET(pthread_mutex_destroy(&e->u.mutex));
|
||||
break;
|
||||
case check_lock_spinlock:
|
||||
LOCKRET(pthread_spin_destroy(&e->u.spinlock));
|
||||
break;
|
||||
case check_lock_rwlock:
|
||||
LOCKRET(pthread_rwlock_destroy(&e->u.rwlock));
|
||||
break;
|
||||
default:
|
||||
log_assert(0);
|
||||
}
|
||||
memset(e, 0, sizeof(struct checked_lock));
|
||||
free(e);
|
||||
}
|
||||
|
||||
/** finish acquiring lock, shared between _(rd|wr||)lock() routines */
|
||||
static void
|
||||
finish_acquire_lock(struct thr_check* thr, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
thr->waiting = NULL;
|
||||
lock->wait_count --;
|
||||
lock->holder = thr;
|
||||
lock->hold_count ++;
|
||||
lock->holder_func = func;
|
||||
lock->holder_file = file;
|
||||
lock->holder_line = line;
|
||||
ordercheck_locklock(thr, lock);
|
||||
|
||||
/* insert in thread lock list, as first */
|
||||
lock->prev_held_lock[thr->num] = NULL;
|
||||
lock->next_held_lock[thr->num] = thr->holding_first;
|
||||
if(thr->holding_first)
|
||||
/* no need to lock it, since this thread already holds the
|
||||
* lock (since it is on this list) and we only edit thr->num
|
||||
* member in array. So it is safe. */
|
||||
thr->holding_first->prev_held_lock[thr->num] = lock;
|
||||
else thr->holding_last = lock;
|
||||
thr->holding_first = lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locking routine.
|
||||
* @param type: as passed by user.
|
||||
* @param lock: as passed by user.
|
||||
* @param func: caller location.
|
||||
* @param file: caller location.
|
||||
* @param line: caller location.
|
||||
* @param tryfunc: the pthread_mutex_trylock or similar function.
|
||||
* @param timedfunc: the pthread_mutex_timedlock or similar function.
|
||||
* Uses absolute timeout value.
|
||||
* @param arg: what to pass to tryfunc and timedlock.
|
||||
* @param exclusive: if lock must be exlusive (only one allowed).
|
||||
* @param getwr: if attempts to get writelock (or readlock) for rwlocks.
|
||||
*/
|
||||
static void
|
||||
checklock_lockit(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line,
|
||||
int (*tryfunc)(void*), int (*timedfunc)(void*, struct timespec*),
|
||||
void* arg, int exclusive, int getwr)
|
||||
{
|
||||
int err;
|
||||
int contend = 0;
|
||||
struct thr_check *thr = (struct thr_check*)pthread_getspecific(
|
||||
thr_debug_key);
|
||||
checktype(type, lock, func, file, line);
|
||||
if(!thr) lock_error(lock, func, file, line, "no thread info");
|
||||
|
||||
acquire_locklock(lock, func, file, line);
|
||||
lock->wait_count ++;
|
||||
thr->waiting = lock;
|
||||
if(exclusive && lock->hold_count > 0 && lock->holder == thr)
|
||||
lock_error(lock, func, file, line, "thread already owns lock");
|
||||
if(type==check_lock_rwlock && getwr && lock->writeholder == thr)
|
||||
lock_error(lock, func, file, line, "thread already has wrlock");
|
||||
LOCKRET(pthread_mutex_unlock(&lock->lock));
|
||||
|
||||
/* first try; if busy increase contention counter */
|
||||
if((err=tryfunc(arg))) {
|
||||
struct timespec to;
|
||||
if(err != EBUSY) log_err("trylock: %s", strerror(err));
|
||||
to.tv_sec = time(NULL) + CHECK_LOCK_TIMEOUT;
|
||||
to.tv_nsec = 0;
|
||||
if((err=timedfunc(arg, &to))) {
|
||||
if(err == ETIMEDOUT)
|
||||
lock_error(lock, func, file, line,
|
||||
"timeout possible deadlock");
|
||||
log_err("timedlock: %s", strerror(err));
|
||||
}
|
||||
contend ++;
|
||||
}
|
||||
/* got the lock */
|
||||
|
||||
acquire_locklock(lock, func, file, line);
|
||||
lock->contention_count += contend;
|
||||
lock->history_count++;
|
||||
if(exclusive && lock->hold_count > 0)
|
||||
lock_error(lock, func, file, line, "got nonexclusive lock");
|
||||
if(type==check_lock_rwlock && getwr && lock->writeholder)
|
||||
lock_error(lock, func, file, line, "got nonexclusive wrlock");
|
||||
if(type==check_lock_rwlock && getwr)
|
||||
lock->writeholder = thr;
|
||||
/* check the memory areas for unauthorized changes,
|
||||
* between last unlock time and current lock time.
|
||||
* we check while holding the lock (threadsafe).
|
||||
*/
|
||||
if(getwr || exclusive)
|
||||
prot_check(lock, func, file, line);
|
||||
finish_acquire_lock(thr, lock, func, file, line);
|
||||
LOCKRET(pthread_mutex_unlock(&lock->lock));
|
||||
}
|
||||
|
||||
/** helper for rdlock: try */
|
||||
static int try_rd(void* arg)
|
||||
{ return pthread_rwlock_tryrdlock((pthread_rwlock_t*)arg); }
|
||||
/** helper for rdlock: timed */
|
||||
static int timed_rd(void* arg, struct timespec* to)
|
||||
{ return pthread_rwlock_timedrdlock((pthread_rwlock_t*)arg, to); }
|
||||
|
||||
/** check if OK, lock */
|
||||
void
|
||||
checklock_rdlock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
|
||||
log_assert(type == check_lock_rwlock);
|
||||
checklock_lockit(type, lock, func, file, line,
|
||||
try_rd, timed_rd, &lock->u.rwlock, 0, 0);
|
||||
}
|
||||
|
||||
/** helper for wrlock: try */
|
||||
static int try_wr(void* arg)
|
||||
{ return pthread_rwlock_trywrlock((pthread_rwlock_t*)arg); }
|
||||
/** helper for wrlock: timed */
|
||||
static int timed_wr(void* arg, struct timespec* to)
|
||||
{ return pthread_rwlock_timedwrlock((pthread_rwlock_t*)arg, to); }
|
||||
|
||||
/** check if OK, lock */
|
||||
void
|
||||
checklock_wrlock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
log_assert(type == check_lock_rwlock);
|
||||
checklock_lockit(type, lock, func, file, line,
|
||||
try_wr, timed_wr, &lock->u.rwlock, 0, 1);
|
||||
}
|
||||
|
||||
/** helper for lock mutex: try */
|
||||
static int try_mutex(void* arg)
|
||||
{ return pthread_mutex_trylock((pthread_mutex_t*)arg); }
|
||||
/** helper for lock mutex: timed */
|
||||
static int timed_mutex(void* arg, struct timespec* to)
|
||||
{ return pthread_mutex_timedlock((pthread_mutex_t*)arg, to); }
|
||||
|
||||
/** helper for lock spinlock: try */
|
||||
static int try_spinlock(void* arg)
|
||||
{ return pthread_spin_trylock((pthread_spinlock_t*)arg); }
|
||||
/** helper for lock spinlock: timed */
|
||||
static int timed_spinlock(void* arg, struct timespec* to)
|
||||
{
|
||||
int err;
|
||||
/* spin for 5 seconds. (ouch for the CPU, but it beats forever) */
|
||||
while( (err=try_spinlock(arg)) == EBUSY) {
|
||||
#ifndef S_SPLINT_S
|
||||
if(time(NULL) >= to->tv_sec)
|
||||
return ETIMEDOUT;
|
||||
usleep(1000); /* in 1/1000000s of a second */
|
||||
#endif
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/** check if OK, lock */
|
||||
void
|
||||
checklock_lock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
log_assert(type != check_lock_rwlock);
|
||||
switch(type) {
|
||||
case check_lock_mutex:
|
||||
checklock_lockit(type, lock, func, file, line,
|
||||
try_mutex, timed_mutex, &lock->u.mutex, 1, 0);
|
||||
break;
|
||||
case check_lock_spinlock:
|
||||
/* void* cast needed because 'volatile' on some OS */
|
||||
checklock_lockit(type, lock, func, file, line,
|
||||
try_spinlock, timed_spinlock,
|
||||
(void*)&lock->u.spinlock, 1, 0);
|
||||
break;
|
||||
default:
|
||||
log_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
/** check if OK, unlock */
|
||||
void
|
||||
checklock_unlock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line)
|
||||
{
|
||||
struct thr_check *thr = (struct thr_check*)pthread_getspecific(
|
||||
thr_debug_key);
|
||||
checktype(type, lock, func, file, line);
|
||||
if(!thr) lock_error(lock, func, file, line, "no thread info");
|
||||
|
||||
acquire_locklock(lock, func, file, line);
|
||||
/* was this thread even holding this lock? */
|
||||
if(thr->holding_first != lock &&
|
||||
lock->prev_held_lock[thr->num] == NULL) {
|
||||
lock_error(lock, func, file, line, "unlock nonlocked lock");
|
||||
}
|
||||
if(lock->hold_count <= 0)
|
||||
lock_error(lock, func, file, line, "too many unlocks");
|
||||
|
||||
/* store this point as last touched by */
|
||||
lock->holder = thr;
|
||||
lock->hold_count --;
|
||||
lock->holder_func = func;
|
||||
lock->holder_file = file;
|
||||
lock->holder_line = line;
|
||||
|
||||
/* delete from thread holder list */
|
||||
/* no need to lock other lockstructs, because they are all on the
|
||||
* held-locks list, and this thread holds their locks.
|
||||
* we only touch the thr->num members, so it is safe. */
|
||||
if(thr->holding_first == lock)
|
||||
thr->holding_first = lock->next_held_lock[thr->num];
|
||||
if(thr->holding_last == lock)
|
||||
thr->holding_last = lock->prev_held_lock[thr->num];
|
||||
if(lock->next_held_lock[thr->num])
|
||||
lock->next_held_lock[thr->num]->prev_held_lock[thr->num] =
|
||||
lock->prev_held_lock[thr->num];
|
||||
if(lock->prev_held_lock[thr->num])
|
||||
lock->prev_held_lock[thr->num]->next_held_lock[thr->num] =
|
||||
lock->next_held_lock[thr->num];
|
||||
lock->next_held_lock[thr->num] = NULL;
|
||||
lock->prev_held_lock[thr->num] = NULL;
|
||||
|
||||
if(type==check_lock_rwlock && lock->writeholder == thr) {
|
||||
lock->writeholder = NULL;
|
||||
prot_store(lock);
|
||||
} else if(type != check_lock_rwlock) {
|
||||
/* store memory areas that are protected, for later checks */
|
||||
prot_store(lock);
|
||||
}
|
||||
LOCKRET(pthread_mutex_unlock(&lock->lock));
|
||||
|
||||
/* unlock it */
|
||||
switch(type) {
|
||||
case check_lock_mutex:
|
||||
LOCKRET(pthread_mutex_unlock(&lock->u.mutex));
|
||||
break;
|
||||
case check_lock_spinlock:
|
||||
LOCKRET(pthread_spin_unlock(&lock->u.spinlock));
|
||||
break;
|
||||
case check_lock_rwlock:
|
||||
LOCKRET(pthread_rwlock_unlock(&lock->u.rwlock));
|
||||
break;
|
||||
default:
|
||||
log_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
/** open order info debug file, thr->num must be valid */
|
||||
static void
|
||||
open_lockorder(struct thr_check* thr)
|
||||
{
|
||||
char buf[24];
|
||||
time_t t;
|
||||
snprintf(buf, sizeof(buf), "ublocktrace.%d", thr->num);
|
||||
thr->order_info = fopen(buf, "w");
|
||||
if(!thr->order_info)
|
||||
fatal_exit("could not open %s: %s", buf, strerror(errno));
|
||||
thr->locks_created = 0;
|
||||
t = time(NULL);
|
||||
/* write: <time_stamp> <runpid> <thread_num> */
|
||||
if(fwrite(&t, sizeof(t), 1, thr->order_info) != 1 ||
|
||||
fwrite(&thr->num, sizeof(thr->num), 1, thr->order_info) != 1 ||
|
||||
fwrite(&check_lock_pid, sizeof(check_lock_pid), 1,
|
||||
thr->order_info) != 1)
|
||||
log_err("fwrite: %s", strerror(errno));
|
||||
}
|
||||
|
||||
/** checklock thread main, Inits thread structure */
|
||||
static void* checklock_main(void* arg)
|
||||
{
|
||||
struct thr_check* thr = (struct thr_check*)arg;
|
||||
void* ret;
|
||||
thr->id = pthread_self();
|
||||
/* Hack to get same numbers as in log file */
|
||||
thr->num = *(int*)(thr->arg);
|
||||
log_assert(thr->num < THRDEBUG_MAX_THREADS);
|
||||
/* as an aside, due to this, won't work for libunbound bg thread */
|
||||
if(thread_infos[thr->num] != NULL)
|
||||
log_warn("thread warning, thr->num %d not NULL", thr->num);
|
||||
thread_infos[thr->num] = thr;
|
||||
LOCKRET(pthread_setspecific(thr_debug_key, thr));
|
||||
if(check_locking_order)
|
||||
open_lockorder(thr);
|
||||
ret = thr->func(thr->arg);
|
||||
thread_infos[thr->num] = NULL;
|
||||
if(check_locking_order)
|
||||
fclose(thr->order_info);
|
||||
free(thr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** init the main thread */
|
||||
void checklock_start(void)
|
||||
{
|
||||
if(key_deleted)
|
||||
return;
|
||||
if(!key_created) {
|
||||
struct thr_check* thisthr = (struct thr_check*)calloc(1,
|
||||
sizeof(struct thr_check));
|
||||
if(!thisthr)
|
||||
fatal_exit("thrcreate: out of memory");
|
||||
key_created = 1;
|
||||
check_lock_pid = getpid();
|
||||
LOCKRET(pthread_key_create(&thr_debug_key, NULL));
|
||||
LOCKRET(pthread_setspecific(thr_debug_key, thisthr));
|
||||
thread_infos[0] = thisthr;
|
||||
if(check_locking_order)
|
||||
open_lockorder(thisthr);
|
||||
}
|
||||
}
|
||||
|
||||
/** stop checklocks */
|
||||
void checklock_stop(void)
|
||||
{
|
||||
if(key_created) {
|
||||
int i;
|
||||
key_deleted = 1;
|
||||
if(check_locking_order)
|
||||
fclose(thread_infos[0]->order_info);
|
||||
free(thread_infos[0]);
|
||||
thread_infos[0] = NULL;
|
||||
for(i = 0; i < THRDEBUG_MAX_THREADS; i++)
|
||||
log_assert(thread_infos[i] == NULL);
|
||||
/* should have been cleaned up. */
|
||||
LOCKRET(pthread_key_delete(thr_debug_key));
|
||||
key_created = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** allocate debug info and create thread */
|
||||
void
|
||||
checklock_thrcreate(pthread_t* id, void* (*func)(void*), void* arg)
|
||||
{
|
||||
struct thr_check* thr = (struct thr_check*)calloc(1,
|
||||
sizeof(struct thr_check));
|
||||
if(!thr)
|
||||
fatal_exit("thrcreate: out of memory");
|
||||
if(!key_created) {
|
||||
checklock_start();
|
||||
}
|
||||
thr->func = func;
|
||||
thr->arg = arg;
|
||||
LOCKRET(pthread_create(id, NULL, checklock_main, thr));
|
||||
}
|
||||
|
||||
/** count number of thread infos */
|
||||
static int
|
||||
count_thread_infos(void)
|
||||
{
|
||||
int cnt = 0;
|
||||
int i;
|
||||
for(i=0; i<THRDEBUG_MAX_THREADS; i++)
|
||||
if(thread_infos[i])
|
||||
cnt++;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/** print lots of info on a lock */
|
||||
static void
|
||||
lock_debug_info(struct checked_lock* lock)
|
||||
{
|
||||
if(!lock) return;
|
||||
log_info("+++ Lock %x, %d %d create %s %s %d", (int)lock,
|
||||
lock->create_thread, lock->create_instance,
|
||||
lock->create_func, lock->create_file, lock->create_line);
|
||||
log_info("lock type: %s",
|
||||
(lock->type==check_lock_mutex)?"mutex": (
|
||||
(lock->type==check_lock_spinlock)?"spinlock": (
|
||||
(lock->type==check_lock_rwlock)?"rwlock": "badtype")));
|
||||
log_info("lock contention %u, history:%u, hold:%d, wait:%d",
|
||||
(unsigned)lock->contention_count, (unsigned)lock->history_count,
|
||||
lock->hold_count, lock->wait_count);
|
||||
log_info("last touch %s %s %d", lock->holder_func, lock->holder_file,
|
||||
lock->holder_line);
|
||||
log_info("holder thread %d, writeholder thread %d",
|
||||
lock->holder?lock->holder->num:-1,
|
||||
lock->writeholder?lock->writeholder->num:-1);
|
||||
}
|
||||
|
||||
/** print debug locks held by a thread */
|
||||
static void
|
||||
held_debug_info(struct thr_check* thr, struct checked_lock* lock)
|
||||
{
|
||||
if(!lock) return;
|
||||
lock_debug_info(lock);
|
||||
held_debug_info(thr, lock->next_held_lock[thr->num]);
|
||||
}
|
||||
|
||||
/** print debug info for a thread */
|
||||
static void
|
||||
thread_debug_info(struct thr_check* thr)
|
||||
{
|
||||
struct checked_lock* w = NULL;
|
||||
struct checked_lock* f = NULL;
|
||||
struct checked_lock* l = NULL;
|
||||
if(!thr) return;
|
||||
log_info("pthread id is %x", (int)thr->id);
|
||||
log_info("thread func is %x", (int)thr->func);
|
||||
log_info("thread arg is %x (%d)", (int)thr->arg,
|
||||
(thr->arg?*(int*)thr->arg:0));
|
||||
log_info("thread num is %d", thr->num);
|
||||
log_info("locks created %d", thr->locks_created);
|
||||
log_info("open file for lockinfo: %s",
|
||||
thr->order_info?"yes, flushing":"no");
|
||||
fflush(thr->order_info);
|
||||
w = thr->waiting;
|
||||
f = thr->holding_first;
|
||||
l = thr->holding_last;
|
||||
log_info("thread waiting for a lock: %s %x", w?"yes":"no", (int)w);
|
||||
lock_debug_info(w);
|
||||
log_info("thread holding first: %s, last: %s", f?"yes":"no",
|
||||
l?"yes":"no");
|
||||
held_debug_info(thr, f);
|
||||
}
|
||||
|
||||
static void
|
||||
total_debug_info(void)
|
||||
{
|
||||
int i;
|
||||
log_info("checklocks: supervising %d threads.",
|
||||
count_thread_infos());
|
||||
if(!key_created) {
|
||||
log_info("No thread debug key created yet");
|
||||
}
|
||||
for(i=0; i<THRDEBUG_MAX_THREADS; i++) {
|
||||
if(thread_infos[i]) {
|
||||
log_info("*** Thread %d information: ***", i);
|
||||
thread_debug_info(thread_infos[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** signal handler for join timeout, Exits */
|
||||
static RETSIGTYPE joinalarm(int ATTR_UNUSED(sig))
|
||||
{
|
||||
log_err("join thread timeout. hangup or deadlock. Info follows.");
|
||||
total_debug_info();
|
||||
fatal_exit("join thread timeout. hangup or deadlock.");
|
||||
}
|
||||
|
||||
/** wait for thread with a timeout */
|
||||
void
|
||||
checklock_thrjoin(pthread_t thread)
|
||||
{
|
||||
/* wait with a timeout */
|
||||
if(signal(SIGALRM, joinalarm) == SIG_ERR)
|
||||
fatal_exit("signal(): %s", strerror(errno));
|
||||
(void)alarm(CHECK_JOIN_TIMEOUT);
|
||||
LOCKRET(pthread_join(thread, NULL));
|
||||
(void)alarm(0);
|
||||
}
|
||||
|
||||
#endif /* USE_THREAD_DEBUG */
|
||||
343
external/unbound/testcode/checklocks.h
vendored
Normal file
343
external/unbound/testcode/checklocks.h
vendored
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
/**
|
||||
* testcode/checklocks.h - wrapper on locks that checks access.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef TESTCODE_CHECK_LOCKS_H
|
||||
#define TESTCODE_CHECK_LOCKS_H
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Locks that are checked.
|
||||
*
|
||||
* Holds information per lock and per thread.
|
||||
* That information is protected by a mutex (unchecked).
|
||||
*
|
||||
* Checks:
|
||||
* o which func, file, line created the lock.
|
||||
* o contention count, measures amount of contention on the lock.
|
||||
* o the memory region(s) that the lock protects are
|
||||
* memcmp'ed to ascertain no race conditions.
|
||||
* o checks that locks are unlocked properly (before deletion).
|
||||
* keeps which func, file, line that locked it.
|
||||
* o checks deadlocks with timeout so it can print errors for them.
|
||||
*
|
||||
* Limitations:
|
||||
* o Detects unprotected memory access when the lock is locked or freed,
|
||||
* which detects races only if they happen, and only if in protected
|
||||
* memory areas.
|
||||
* o Detects deadlocks by timeout, so approximately, as they happen.
|
||||
* o Does not check order of locking.
|
||||
* o Uses a lot of memory.
|
||||
* o The checks use locks themselves, changing scheduling,
|
||||
* thus changing what races you see.
|
||||
*/
|
||||
|
||||
#ifdef USE_THREAD_DEBUG
|
||||
#ifndef HAVE_PTHREAD
|
||||
/* we need the *timed*lock() routines to use for deadlock detection. */
|
||||
#error "Need pthreads for checked locks"
|
||||
#endif
|
||||
/******************* THREAD DEBUG ************************/
|
||||
#include <pthread.h>
|
||||
|
||||
/** How many threads to allocate for */
|
||||
#define THRDEBUG_MAX_THREADS 32 /* threads */
|
||||
/** do we check locking order */
|
||||
extern int check_locking_order;
|
||||
|
||||
/**
|
||||
* Protection memory area.
|
||||
* It is copied to a holding buffer to compare against later.
|
||||
* Note that it may encompass the lock structure.
|
||||
*/
|
||||
struct protected_area {
|
||||
/** where the memory region starts */
|
||||
void* region;
|
||||
/** size of the region */
|
||||
size_t size;
|
||||
/** backbuffer that holds a copy, of same size. */
|
||||
void* hold;
|
||||
/** next protected area in list */
|
||||
struct protected_area* next;
|
||||
};
|
||||
|
||||
/**
|
||||
* Per thread information for locking debug wrappers.
|
||||
*/
|
||||
struct thr_check {
|
||||
/** thread id */
|
||||
pthread_t id;
|
||||
/** real thread func */
|
||||
void* (*func)(void*);
|
||||
/** func user arg */
|
||||
void* arg;
|
||||
/** number of thread in list structure */
|
||||
int num;
|
||||
/** instance number - how many locks have been created by thread */
|
||||
int locks_created;
|
||||
/** file to write locking order information to */
|
||||
FILE* order_info;
|
||||
/**
|
||||
* List of locks that this thread is holding, double
|
||||
* linked list. The first element is the most recent lock acquired.
|
||||
* So it represents the stack of locks acquired. (of all types).
|
||||
*/
|
||||
struct checked_lock *holding_first, *holding_last;
|
||||
/** if the thread is currently waiting for a lock, which one */
|
||||
struct checked_lock* waiting;
|
||||
};
|
||||
|
||||
/**
|
||||
* One structure for all types of locks.
|
||||
*/
|
||||
struct checked_lock {
|
||||
/** mutex for exclusive access to this structure */
|
||||
pthread_mutex_t lock;
|
||||
/** list of memory regions protected by this checked lock */
|
||||
struct protected_area* prot;
|
||||
/** where was this lock created */
|
||||
const char* create_func, *create_file;
|
||||
/** where was this lock created */
|
||||
int create_line;
|
||||
/** unique instance identifier */
|
||||
int create_thread, create_instance;
|
||||
/** contention count */
|
||||
size_t contention_count;
|
||||
/** number of times locked, ever */
|
||||
size_t history_count;
|
||||
/** hold count (how many threads are holding this lock) */
|
||||
int hold_count;
|
||||
/** how many threads are waiting for this lock */
|
||||
int wait_count;
|
||||
/** who touched it last */
|
||||
const char* holder_func, *holder_file;
|
||||
/** who touched it last */
|
||||
int holder_line;
|
||||
/** who owns the lock now */
|
||||
struct thr_check* holder;
|
||||
/** for rwlocks, the writelock holder */
|
||||
struct thr_check* writeholder;
|
||||
|
||||
/** next lock a thread is holding (less recent) */
|
||||
struct checked_lock* next_held_lock[THRDEBUG_MAX_THREADS];
|
||||
/** prev lock a thread is holding (more recent) */
|
||||
struct checked_lock* prev_held_lock[THRDEBUG_MAX_THREADS];
|
||||
|
||||
/** type of lock */
|
||||
enum check_lock_type {
|
||||
/** basic mutex */
|
||||
check_lock_mutex,
|
||||
/** fast spinlock */
|
||||
check_lock_spinlock,
|
||||
/** rwlock */
|
||||
check_lock_rwlock
|
||||
} type;
|
||||
/** the lock itself, see type to disambiguate the union */
|
||||
union {
|
||||
/** mutex */
|
||||
pthread_mutex_t mutex;
|
||||
/** spinlock */
|
||||
pthread_spinlock_t spinlock;
|
||||
/** rwlock */
|
||||
pthread_rwlock_t rwlock;
|
||||
} u;
|
||||
};
|
||||
|
||||
/**
|
||||
* Additional call for the user to specify what areas are protected
|
||||
* @param lock: the lock that protects the area. It can be inside the area.
|
||||
* The lock must be inited. Call with user lock. (any type).
|
||||
* It demangles the lock itself (struct checked_lock**).
|
||||
* @param area: ptr to mem.
|
||||
* @param size: length of area.
|
||||
* You can call it multiple times with the same lock to give several areas.
|
||||
* Call it when you are done initialising the area, since it will be copied
|
||||
* at this time and protected right away against unauthorised changes until
|
||||
* the next lock() call is done.
|
||||
*/
|
||||
void lock_protect(void* lock, void* area, size_t size);
|
||||
|
||||
/**
|
||||
* Remove protected area from lock.
|
||||
* No need to call this when deleting the lock.
|
||||
* @param lock: the lock, any type, (struct checked_lock**).
|
||||
* @param area: pointer to memory.
|
||||
*/
|
||||
void lock_unprotect(void* lock, void* area);
|
||||
|
||||
/**
|
||||
* Get memory associated with a checked lock
|
||||
* @param lock: the checked lock, any type. (struct checked_lock**).
|
||||
* @return: in bytes, including protected areas.
|
||||
*/
|
||||
size_t lock_get_mem(void* lock);
|
||||
|
||||
/**
|
||||
* Initialise checklock. Sets up internal debug structures.
|
||||
*/
|
||||
void checklock_start(void);
|
||||
|
||||
/**
|
||||
* Cleanup internal debug state.
|
||||
*/
|
||||
void checklock_stop(void);
|
||||
|
||||
/**
|
||||
* Init locks.
|
||||
* @param type: what type of lock this is.
|
||||
* @param lock: ptr to user alloced ptr structure. This is inited.
|
||||
* So an alloc is done and the ptr is stored as result.
|
||||
* @param func: caller function name.
|
||||
* @param file: caller file name.
|
||||
* @param line: caller line number.
|
||||
*/
|
||||
void checklock_init(enum check_lock_type type, struct checked_lock** lock,
|
||||
const char* func, const char* file, int line);
|
||||
|
||||
/**
|
||||
* Destroy locks. Free the structure.
|
||||
* @param type: what type of lock this is.
|
||||
* @param lock: ptr to user alloced structure. This is destroyed.
|
||||
* @param func: caller function name.
|
||||
* @param file: caller file name.
|
||||
* @param line: caller line number.
|
||||
*/
|
||||
void checklock_destroy(enum check_lock_type type, struct checked_lock** lock,
|
||||
const char* func, const char* file, int line);
|
||||
|
||||
/**
|
||||
* Acquire readlock.
|
||||
* @param type: what type of lock this is. Had better be a rwlock.
|
||||
* @param lock: ptr to lock.
|
||||
* @param func: caller function name.
|
||||
* @param file: caller file name.
|
||||
* @param line: caller line number.
|
||||
*/
|
||||
void checklock_rdlock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line);
|
||||
|
||||
/**
|
||||
* Acquire writelock.
|
||||
* @param type: what type of lock this is. Had better be a rwlock.
|
||||
* @param lock: ptr to lock.
|
||||
* @param func: caller function name.
|
||||
* @param file: caller file name.
|
||||
* @param line: caller line number.
|
||||
*/
|
||||
void checklock_wrlock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line);
|
||||
|
||||
/**
|
||||
* Locks.
|
||||
* @param type: what type of lock this is. Had better be mutex or spinlock.
|
||||
* @param lock: the lock.
|
||||
* @param func: caller function name.
|
||||
* @param file: caller file name.
|
||||
* @param line: caller line number.
|
||||
*/
|
||||
void checklock_lock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line);
|
||||
|
||||
/**
|
||||
* Unlocks.
|
||||
* @param type: what type of lock this is.
|
||||
* @param lock: the lock.
|
||||
* @param func: caller function name.
|
||||
* @param file: caller file name.
|
||||
* @param line: caller line number.
|
||||
*/
|
||||
void checklock_unlock(enum check_lock_type type, struct checked_lock* lock,
|
||||
const char* func, const char* file, int line);
|
||||
|
||||
/**
|
||||
* Create thread.
|
||||
* @param thr: Thread id, where to store result.
|
||||
* @param func: thread start function.
|
||||
* @param arg: user argument.
|
||||
*/
|
||||
void checklock_thrcreate(pthread_t* thr, void* (*func)(void*), void* arg);
|
||||
|
||||
/**
|
||||
* Wait for thread to exit. Returns thread return value.
|
||||
* @param thread: thread to wait for.
|
||||
*/
|
||||
void checklock_thrjoin(pthread_t thread);
|
||||
|
||||
/** structures to enable compiler type checking on the locks.
|
||||
* Also the pointer makes it so that the lock can be part of the protected
|
||||
* region without any possible problem (since the ptr will stay the same.)
|
||||
* i.e. there can be contention and readlocks stored in checked_lock, while
|
||||
* the protected area stays the same, even though it contains (ptr to) lock.
|
||||
*/
|
||||
struct checked_lock_rw { struct checked_lock* c_rw; };
|
||||
/** structures to enable compiler type checking on the locks. */
|
||||
struct checked_lock_mutex { struct checked_lock* c_m; };
|
||||
/** structures to enable compiler type checking on the locks. */
|
||||
struct checked_lock_spl { struct checked_lock* c_spl; };
|
||||
|
||||
/** debugging rwlock */
|
||||
typedef struct checked_lock_rw lock_rw_t;
|
||||
#define lock_rw_init(lock) checklock_init(check_lock_rwlock, &((lock)->c_rw), __func__, __FILE__, __LINE__)
|
||||
#define lock_rw_destroy(lock) checklock_destroy(check_lock_rwlock, &((lock)->c_rw), __func__, __FILE__, __LINE__)
|
||||
#define lock_rw_rdlock(lock) checklock_rdlock(check_lock_rwlock, (lock)->c_rw, __func__, __FILE__, __LINE__)
|
||||
#define lock_rw_wrlock(lock) checklock_wrlock(check_lock_rwlock, (lock)->c_rw, __func__, __FILE__, __LINE__)
|
||||
#define lock_rw_unlock(lock) checklock_unlock(check_lock_rwlock, (lock)->c_rw, __func__, __FILE__, __LINE__)
|
||||
|
||||
/** debugging mutex */
|
||||
typedef struct checked_lock_mutex lock_basic_t;
|
||||
#define lock_basic_init(lock) checklock_init(check_lock_mutex, &((lock)->c_m), __func__, __FILE__, __LINE__)
|
||||
#define lock_basic_destroy(lock) checklock_destroy(check_lock_mutex, &((lock)->c_m), __func__, __FILE__, __LINE__)
|
||||
#define lock_basic_lock(lock) checklock_lock(check_lock_mutex, (lock)->c_m, __func__, __FILE__, __LINE__)
|
||||
#define lock_basic_unlock(lock) checklock_unlock(check_lock_mutex, (lock)->c_m, __func__, __FILE__, __LINE__)
|
||||
|
||||
/** debugging spinlock */
|
||||
typedef struct checked_lock_spl lock_quick_t;
|
||||
#define lock_quick_init(lock) checklock_init(check_lock_spinlock, &((lock)->c_spl), __func__, __FILE__, __LINE__)
|
||||
#define lock_quick_destroy(lock) checklock_destroy(check_lock_spinlock, &((lock)->c_spl), __func__, __FILE__, __LINE__)
|
||||
#define lock_quick_lock(lock) checklock_lock(check_lock_spinlock, (lock)->c_spl, __func__, __FILE__, __LINE__)
|
||||
#define lock_quick_unlock(lock) checklock_unlock(check_lock_spinlock, (lock)->c_spl, __func__, __FILE__, __LINE__)
|
||||
|
||||
/** we use the pthread id, our thr_check structure is kept behind the scenes */
|
||||
typedef pthread_t ub_thread_t;
|
||||
#define ub_thread_create(thr, func, arg) checklock_thrcreate(thr, func, arg)
|
||||
#define ub_thread_self() pthread_self()
|
||||
#define ub_thread_join(thread) checklock_thrjoin(thread)
|
||||
|
||||
typedef pthread_key_t ub_thread_key_t;
|
||||
#define ub_thread_key_create(key, f) LOCKRET(pthread_key_create(key, f))
|
||||
#define ub_thread_key_set(key, v) LOCKRET(pthread_setspecific(key, v))
|
||||
#define ub_thread_key_get(key) pthread_getspecific(key)
|
||||
|
||||
#endif /* USE_THREAD_DEBUG */
|
||||
#endif /* TESTCODE_CHECK_LOCKS_H */
|
||||
1186
external/unbound/testcode/delayer.c
vendored
Normal file
1186
external/unbound/testcode/delayer.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
61
external/unbound/testcode/do-tests.sh
vendored
Executable file
61
external/unbound/testcode/do-tests.sh
vendored
Executable file
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env bash
|
||||
. testdata/common.sh
|
||||
|
||||
NEED_SPLINT='00-lint.tpkg'
|
||||
NEED_DOXYGEN='01-doc.tpkg'
|
||||
NEED_XXD='fwd_compress_c00c.tpkg fwd_zero.tpkg'
|
||||
NEED_NC='fwd_compress_c00c.tpkg fwd_zero.tpkg'
|
||||
NEED_CURL='06-ianaports.tpkg root_anchor.tpkg'
|
||||
NEED_WHOAMI='07-confroot.tpkg'
|
||||
NEED_IPV6='fwd_ancil.tpkg fwd_tcp_tc6.tpkg stub_udp6.tpkg edns_cache.tpkg'
|
||||
NEED_NOMINGW='tcp_sigpipe.tpkg 07-confroot.tpkg 08-host-lib.tpkg fwd_ancil.tpkg'
|
||||
|
||||
# test if dig and ldns-testns are available.
|
||||
test_tool_avail "dig"
|
||||
test_tool_avail "ldns-testns"
|
||||
|
||||
# test for ipv6, uses streamptcp peculiarity.
|
||||
if ./streamtcp -f ::1 2>&1 | grep "not supported" >/dev/null 2>&1; then
|
||||
HAVE_IPV6=no
|
||||
else
|
||||
HAVE_IPV6=yes
|
||||
fi
|
||||
|
||||
# test mingw. no signals and so on.
|
||||
if uname | grep MINGW >/dev/null; then
|
||||
HAVE_MINGW=yes
|
||||
else
|
||||
HAVE_MINGW=no
|
||||
fi
|
||||
|
||||
cd testdata;
|
||||
sh ../testcode/mini_tpkg.sh clean
|
||||
rm -f .perfstats.txt
|
||||
for test in `ls *.tpkg`; do
|
||||
SKIP=0
|
||||
skip_if_in_list $test "$NEED_SPLINT" "splint"
|
||||
skip_if_in_list $test "$NEED_DOXYGEN" "doxygen"
|
||||
skip_if_in_list $test "$NEED_CURL" "curl"
|
||||
skip_if_in_list $test "$NEED_XXD" "xxd"
|
||||
skip_if_in_list $test "$NEED_NC" "nc"
|
||||
skip_if_in_list $test "$NEED_WHOAMI" "whoami"
|
||||
|
||||
if echo $NEED_IPV6 | grep $test >/dev/null; then
|
||||
if test "$HAVE_IPV6" = no; then
|
||||
SKIP=1;
|
||||
fi
|
||||
fi
|
||||
if echo $NEED_NOMINGW | grep $test >/dev/null; then
|
||||
if test "$HAVE_MINGW" = yes; then
|
||||
SKIP=1;
|
||||
fi
|
||||
fi
|
||||
if test $SKIP -eq 0; then
|
||||
echo $test
|
||||
sh ../testcode/mini_tpkg.sh -a ../.. exe $test
|
||||
else
|
||||
echo "skip $test"
|
||||
fi
|
||||
done
|
||||
sh ../testcode/mini_tpkg.sh report
|
||||
cat .perfstats.txt
|
||||
1415
external/unbound/testcode/fake_event.c
vendored
Normal file
1415
external/unbound/testcode/fake_event.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
75
external/unbound/testcode/fake_event.h
vendored
Normal file
75
external/unbound/testcode/fake_event.h
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* testcode/fake_event.h - fake event handling that replays existing scenario.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Event service that replays a scenario.
|
||||
* This implements the same exported symbols as the files:
|
||||
* util/netevent.c
|
||||
* services/listen_dnsport.c
|
||||
* services/outside_network.c
|
||||
* But these do not actually access the network or events, instead
|
||||
* the scenario is played.
|
||||
*/
|
||||
|
||||
#ifndef TESTCODE_FAKE_EVENT_H
|
||||
#define TESTCODE_FAKE_EVENT_H
|
||||
struct replay_scenario;
|
||||
|
||||
/**
|
||||
* Initialise fake event services.
|
||||
*
|
||||
* The fake event services will automatically start when the main program
|
||||
* calls netevent.h functions, such as comm_base_dispatch().
|
||||
*
|
||||
* @param scen: Set the scenario to use for upcoming event handling.
|
||||
*/
|
||||
void fake_event_init(struct replay_scenario* scen);
|
||||
|
||||
/**
|
||||
* Deinit fake event services.
|
||||
*/
|
||||
void fake_event_cleanup(void);
|
||||
|
||||
/**
|
||||
* Get filename to store temporary config stuff. The pid is added. in /tmp.
|
||||
* @param adj: adjective, like "_cfg_", "_auto_"
|
||||
* @param id: identifier, like "example.com".
|
||||
* @param buf: where to store.
|
||||
* @param len: length of buf.
|
||||
*/
|
||||
void fake_temp_file(const char* adj, const char* id, char* buf, size_t len);
|
||||
|
||||
#endif /* TESTCODE_FAKE_EVENT_H */
|
||||
423
external/unbound/testcode/lock_verify.c
vendored
Normal file
423
external/unbound/testcode/lock_verify.c
vendored
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
/*
|
||||
* testcode/lock_verify.c - verifier program for lock traces, checks order.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file checks the lock traces generated by checklock.c.
|
||||
* Checks if locks are consistently locked in the same order.
|
||||
* If not, this can lead to deadlock if threads execute the different
|
||||
* ordering at the same time.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/rbtree.h"
|
||||
#include "util/locks.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
|
||||
/* --- data structures --- */
|
||||
struct lock_ref;
|
||||
|
||||
/** keep track of lock id in lock-verify application
|
||||
* Also defined in smallapp/worker_cb.c for fptr_wlist encapsulation
|
||||
* breakage (the security tests break encapsulation for this test app) */
|
||||
struct order_id {
|
||||
/** the thread id that created it */
|
||||
int thr;
|
||||
/** the instance number of creation */
|
||||
int instance;
|
||||
};
|
||||
|
||||
/** a lock */
|
||||
struct order_lock {
|
||||
/** rbnode in all tree */
|
||||
rbnode_t node;
|
||||
/** lock id */
|
||||
struct order_id id;
|
||||
/** the creation file */
|
||||
char* create_file;
|
||||
/** creation line */
|
||||
int create_line;
|
||||
/** set of all locks that are smaller than this one (locked earlier) */
|
||||
rbtree_t* smaller;
|
||||
/** during depthfirstsearch, this is a linked list of the stack
|
||||
* of locks. points to the next lock bigger than this one. */
|
||||
struct lock_ref* dfs_next;
|
||||
/** if lock has been visited (all smaller locks have been compared to
|
||||
* this lock), only need to compare this with all unvisited(bigger)
|
||||
* locks */
|
||||
int visited;
|
||||
};
|
||||
|
||||
/** reference to a lock in a rbtree set */
|
||||
struct lock_ref {
|
||||
/** rbnode, key is an order_id ptr */
|
||||
rbnode_t node;
|
||||
/** the lock referenced */
|
||||
struct order_lock* lock;
|
||||
/** why is this ref */
|
||||
char* file;
|
||||
/** line number */
|
||||
int line;
|
||||
};
|
||||
|
||||
/** count of errors detected */
|
||||
static int errors_detected = 0;
|
||||
/** verbose? */
|
||||
static int verb = 0;
|
||||
|
||||
/** print program usage help */
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
printf("lock_verify <trace files>\n");
|
||||
}
|
||||
|
||||
/** read header entry.
|
||||
* @param in: file to read header of.
|
||||
* @return: False if it does not belong to the rest. */
|
||||
static int
|
||||
read_header(FILE* in)
|
||||
{
|
||||
time_t t;
|
||||
pid_t p;
|
||||
int thrno;
|
||||
static int have_values = 0;
|
||||
static time_t the_time;
|
||||
static pid_t the_pid;
|
||||
static int threads[256];
|
||||
|
||||
if(fread(&t, sizeof(t), 1, in) != 1 ||
|
||||
fread(&thrno, sizeof(thrno), 1, in) != 1 ||
|
||||
fread(&p, sizeof(p), 1, in) != 1) {
|
||||
fatal_exit("fread failed");
|
||||
}
|
||||
/* check these values are sorta OK */
|
||||
if(!have_values) {
|
||||
the_time = t;
|
||||
the_pid = p;
|
||||
memset(threads, 0, 256*sizeof(int));
|
||||
if(thrno >= 256) {
|
||||
fatal_exit("Thread number too big. %d", thrno);
|
||||
}
|
||||
threads[thrno] = 1;
|
||||
have_values = 1;
|
||||
printf(" trace %d from pid %u on %s", thrno,
|
||||
(unsigned)p, ctime(&t));
|
||||
} else {
|
||||
if(the_pid != p) {
|
||||
printf(" has pid %u, not %u. Skipped.\n",
|
||||
(unsigned)p, (unsigned)the_pid);
|
||||
return 0;
|
||||
}
|
||||
if(threads[thrno])
|
||||
fatal_exit("same threadno in two files");
|
||||
threads[thrno] = 1;
|
||||
if( abs((int)(the_time - t)) > 3600)
|
||||
fatal_exit("input files from different times: %u %u",
|
||||
(unsigned)the_time, (unsigned)t);
|
||||
printf(" trace of thread %u:%d\n", (unsigned)p, thrno);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** max length of strings: filenames and function names. */
|
||||
#define STRMAX 1024
|
||||
/** read a string from file, false on error */
|
||||
static int readup_str(char** str, FILE* in)
|
||||
{
|
||||
char buf[STRMAX];
|
||||
int len = 0;
|
||||
int c;
|
||||
/* ends in zero */
|
||||
while( (c = fgetc(in)) != 0) {
|
||||
if(c == EOF)
|
||||
fatal_exit("eof in readstr, file too short");
|
||||
buf[len++] = c;
|
||||
if(len == STRMAX) {
|
||||
fatal_exit("string too long, bad file format");
|
||||
}
|
||||
}
|
||||
buf[len] = 0;
|
||||
*str = strdup(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** read creation entry */
|
||||
static void read_create(rbtree_t* all, FILE* in)
|
||||
{
|
||||
struct order_lock* o = calloc(1, sizeof(struct order_lock));
|
||||
if(!o) fatal_exit("malloc failure");
|
||||
if(fread(&o->id.thr, sizeof(int), 1, in) != 1 ||
|
||||
fread(&o->id.instance, sizeof(int), 1, in) != 1 ||
|
||||
!readup_str(&o->create_file, in) ||
|
||||
fread(&o->create_line, sizeof(int), 1, in) != 1)
|
||||
fatal_exit("fread failed");
|
||||
o->smaller = rbtree_create(order_lock_cmp);
|
||||
o->node.key = &o->id;
|
||||
if(!rbtree_insert(all, &o->node)) {
|
||||
/* already inserted */
|
||||
struct order_lock* a = (struct order_lock*)rbtree_search(all,
|
||||
&o->id);
|
||||
log_assert(a);
|
||||
a->create_file = o->create_file;
|
||||
a->create_line = o->create_line;
|
||||
free(o->smaller);
|
||||
free(o);
|
||||
o = a;
|
||||
}
|
||||
if(verb) printf("read create %u %u %s %d\n",
|
||||
(unsigned)o->id.thr, (unsigned)o->id.instance,
|
||||
o->create_file, o->create_line);
|
||||
}
|
||||
|
||||
/** insert lock entry (empty) into list */
|
||||
static struct order_lock*
|
||||
insert_lock(rbtree_t* all, struct order_id* id)
|
||||
{
|
||||
struct order_lock* o = calloc(1, sizeof(struct order_lock));
|
||||
if(!o) fatal_exit("malloc failure");
|
||||
o->smaller = rbtree_create(order_lock_cmp);
|
||||
o->id = *id;
|
||||
o->node.key = &o->id;
|
||||
if(!rbtree_insert(all, &o->node))
|
||||
fatal_exit("insert fail should not happen");
|
||||
return o;
|
||||
}
|
||||
|
||||
/** read lock entry */
|
||||
static void read_lock(rbtree_t* all, FILE* in, int val)
|
||||
{
|
||||
struct order_id prev_id, now_id;
|
||||
struct lock_ref* ref;
|
||||
struct order_lock* prev, *now;
|
||||
ref = (struct lock_ref*)calloc(1, sizeof(struct lock_ref));
|
||||
if(!ref) fatal_exit("malloc failure");
|
||||
prev_id.thr = val;
|
||||
if(fread(&prev_id.instance, sizeof(int), 1, in) != 1 ||
|
||||
fread(&now_id.thr, sizeof(int), 1, in) != 1 ||
|
||||
fread(&now_id.instance, sizeof(int), 1, in) != 1 ||
|
||||
!readup_str(&ref->file, in) ||
|
||||
fread(&ref->line, sizeof(int), 1, in) != 1)
|
||||
fatal_exit("fread failed");
|
||||
if(verb) printf("read lock %u %u %u %u %s %d\n",
|
||||
(unsigned)prev_id.thr, (unsigned)prev_id.instance,
|
||||
(unsigned)now_id.thr, (unsigned)now_id.instance,
|
||||
ref->file, ref->line);
|
||||
/* find the two locks involved */
|
||||
prev = (struct order_lock*)rbtree_search(all, &prev_id);
|
||||
now = (struct order_lock*)rbtree_search(all, &now_id);
|
||||
/* if not there - insert 'em */
|
||||
if(!prev) prev = insert_lock(all, &prev_id);
|
||||
if(!now) now = insert_lock(all, &now_id);
|
||||
ref->lock = prev;
|
||||
ref->node.key = &prev->id;
|
||||
if(!rbtree_insert(now->smaller, &ref->node)) {
|
||||
free(ref->file);
|
||||
free(ref);
|
||||
}
|
||||
}
|
||||
|
||||
/** read input file */
|
||||
static void readinput(rbtree_t* all, char* file)
|
||||
{
|
||||
FILE *in = fopen(file, "r");
|
||||
int fst;
|
||||
if(!in) {
|
||||
perror(file);
|
||||
exit(1);
|
||||
}
|
||||
printf("file %s", file);
|
||||
if(!read_header(in)) {
|
||||
fclose(in);
|
||||
return;
|
||||
}
|
||||
while(fread(&fst, sizeof(fst), 1, in) == 1) {
|
||||
if(fst == -1)
|
||||
read_create(all, in);
|
||||
else read_lock(all, in, fst);
|
||||
}
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
/** print cycle message */
|
||||
static void found_cycle(struct lock_ref* visit, int level)
|
||||
{
|
||||
struct lock_ref* p;
|
||||
int i = 0;
|
||||
errors_detected++;
|
||||
printf("Found inconsistent locking order of length %d\n", level);
|
||||
printf("for lock %d %d created %s %d\n",
|
||||
visit->lock->id.thr, visit->lock->id.instance,
|
||||
visit->lock->create_file, visit->lock->create_line);
|
||||
printf("sequence is:\n");
|
||||
p = visit;
|
||||
while(p) {
|
||||
struct order_lock* next =
|
||||
p->lock->dfs_next?p->lock->dfs_next->lock:visit->lock;
|
||||
printf("[%d] is locked at line %s %d before lock %d %d\n",
|
||||
i, p->file, p->line, next->id.thr, next->id.instance);
|
||||
printf("[%d] lock %d %d is created at %s %d\n",
|
||||
i, next->id.thr, next->id.instance,
|
||||
next->create_file, next->create_line);
|
||||
i++;
|
||||
p = p->lock->dfs_next;
|
||||
if(p && p->lock == visit->lock)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Detect cycle by comparing visited now with all (unvisited) bigger nodes */
|
||||
static int detect_cycle(struct lock_ref* visit, struct lock_ref* from)
|
||||
{
|
||||
struct lock_ref* p = from;
|
||||
while(p) {
|
||||
if(p->lock == visit->lock)
|
||||
return 1;
|
||||
p = p->lock->dfs_next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** recursive function to depth first search for cycles.
|
||||
* @param visit: the lock visited at this step.
|
||||
* its dfs_next pointer gives the visited lock up in recursion.
|
||||
* same as lookfor at level 0.
|
||||
* @param level: depth of recursion. 0 is start.
|
||||
* @param from: search for matches from unvisited node upwards.
|
||||
*/
|
||||
static void search_cycle(struct lock_ref* visit, int level,
|
||||
struct lock_ref* from)
|
||||
{
|
||||
struct lock_ref* ref;
|
||||
/* check for cycle */
|
||||
if(detect_cycle(visit, from) && level != 0) {
|
||||
found_cycle(visit, level);
|
||||
fatal_exit("found lock order cycle");
|
||||
}
|
||||
/* recurse */
|
||||
if(!visit->lock->visited)
|
||||
from = visit;
|
||||
if(verb > 1) fprintf(stderr, "[%d] visit lock %u %u %s %d\n", level,
|
||||
(unsigned)visit->lock->id.thr,
|
||||
(unsigned)visit->lock->id.instance,
|
||||
visit->lock->create_file, visit->lock->create_line);
|
||||
RBTREE_FOR(ref, struct lock_ref*, visit->lock->smaller) {
|
||||
ref->lock->dfs_next = visit;
|
||||
search_cycle(ref, level+1, from);
|
||||
}
|
||||
visit->lock->visited = 1;
|
||||
}
|
||||
|
||||
/** Check ordering of one lock */
|
||||
static void check_order_lock(struct order_lock* lock)
|
||||
{
|
||||
struct lock_ref start;
|
||||
if(lock->visited) return;
|
||||
|
||||
start.node.key = &lock->id;
|
||||
start.lock = lock;
|
||||
start.file = lock->create_file;
|
||||
start.line = lock->create_line;
|
||||
|
||||
if(!lock->create_file)
|
||||
log_err("lock %u %u does not have create info",
|
||||
(unsigned)lock->id.thr, (unsigned)lock->id.instance);
|
||||
|
||||
/* depth first search to find cycle with this lock at head */
|
||||
lock->dfs_next = NULL;
|
||||
search_cycle(&start, 0, &start);
|
||||
}
|
||||
|
||||
/** Check ordering of locks */
|
||||
static void check_order(rbtree_t* all_locks)
|
||||
{
|
||||
/* check each lock */
|
||||
struct order_lock* lock;
|
||||
int i=0;
|
||||
RBTREE_FOR(lock, struct order_lock*, all_locks) {
|
||||
if(verb)
|
||||
printf("[%d/%d] Checking lock %d %d %s %d\n",
|
||||
i, (int)all_locks->count,
|
||||
lock->id.thr, lock->id.instance,
|
||||
lock->create_file, lock->create_line);
|
||||
else if (i % ((all_locks->count/75)<1?1:all_locks->count/75)
|
||||
== 0)
|
||||
fprintf(stderr, ".");
|
||||
i++;
|
||||
check_order_lock(lock);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
/** main program to verify all traces passed */
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
rbtree_t* all_locks;
|
||||
int i;
|
||||
time_t starttime = time(NULL);
|
||||
#ifdef USE_THREAD_DEBUG
|
||||
/* do not overwrite the ublocktrace files with the ones generated
|
||||
* by this program (i.e. when the log code creates a lock) */
|
||||
check_locking_order = 0;
|
||||
#endif
|
||||
if(argc <= 1) {
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
log_init(NULL, 0, NULL);
|
||||
log_ident_set("lock-verify");
|
||||
/* init */
|
||||
all_locks = rbtree_create(order_lock_cmp);
|
||||
errors_detected = 0;
|
||||
|
||||
/* read the input files */
|
||||
for(i=1; i<argc; i++) {
|
||||
readinput(all_locks, argv[i]);
|
||||
}
|
||||
|
||||
/* check ordering */
|
||||
check_order(all_locks);
|
||||
|
||||
/* do not free a thing, OS will do it */
|
||||
printf("checked %d locks in %d seconds with %d errors.\n",
|
||||
(int)all_locks->count, (int)(time(NULL)-starttime),
|
||||
errors_detected);
|
||||
if(errors_detected) return 1;
|
||||
return 0;
|
||||
}
|
||||
248
external/unbound/testcode/memstats.c
vendored
Normal file
248
external/unbound/testcode/memstats.c
vendored
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* testcode/memstats.c - debug tool to show memory allocation statistics.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This program reads a log file and prints the memory allocation summed
|
||||
* up.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/rbtree.h"
|
||||
#include "util/locks.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
/**
|
||||
* The allocation statistics block
|
||||
*/
|
||||
struct codeline {
|
||||
/** rbtree node */
|
||||
rbnode_t node;
|
||||
/** the name of the file:linenumber */
|
||||
char* codeline;
|
||||
/** the name of the function */
|
||||
char* func;
|
||||
/** number of bytes allocated */
|
||||
uint64_t alloc;
|
||||
/** number of bytes freed */
|
||||
uint64_t free;
|
||||
/** number allocations and frees */
|
||||
uint64_t calls;
|
||||
};
|
||||
|
||||
/** print usage and exit */
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
printf("usage: memstats <logfile>\n");
|
||||
printf("statistics are printed on stdout.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** match logfile line to see if it needs accounting processing */
|
||||
static int
|
||||
match(char* line)
|
||||
{
|
||||
/* f.e.:
|
||||
* [1187340064] unbound[24604:0] info: ul/rb.c:81 r_create malloc(12)
|
||||
* 0123456789 123456789 123456789 123456789
|
||||
* But now also:
|
||||
* Sep 16 15:18:20 unbound[1:0] info: ul/nh.c:143 memdup malloc(11)
|
||||
*/
|
||||
if(strlen(line) < 32) /* up to 'info: ' */
|
||||
return 0;
|
||||
if(!strstr(line, " info: "))
|
||||
return 0;
|
||||
if(strstr(line, "info: stat "))
|
||||
return 0; /* skip the hex dumps */
|
||||
if(strstr(line+30, "malloc("))
|
||||
return 1;
|
||||
else if(strstr(line+30, "calloc("))
|
||||
return 1;
|
||||
/* skip reallocs */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** find or alloc codeline in tree */
|
||||
static struct codeline*
|
||||
get_codeline(rbtree_t* tree, char* key, char* func)
|
||||
{
|
||||
struct codeline* cl = (struct codeline*)rbtree_search(tree, key);
|
||||
if(!cl) {
|
||||
cl = calloc(1, sizeof(*cl));
|
||||
if(!cl) return 0;
|
||||
cl->codeline = strdup(key);
|
||||
if(!cl->codeline) return 0;
|
||||
cl->func = strdup(func);
|
||||
if(!cl->func) return 0;
|
||||
cl->alloc = 0;
|
||||
cl->node.key = cl->codeline;
|
||||
(void)rbtree_insert(tree, &cl->node);
|
||||
}
|
||||
return cl;
|
||||
}
|
||||
|
||||
/** read up the malloc stats */
|
||||
static void
|
||||
read_malloc_stat(char* line, rbtree_t* tree)
|
||||
{
|
||||
char codeline[10240];
|
||||
char name[10240];
|
||||
int skip = 0;
|
||||
long num = 0;
|
||||
struct codeline* cl = 0;
|
||||
line = strstr(line, "info: ")+6;
|
||||
if(sscanf(line, "%s %s %n", codeline, name, &skip) != 2) {
|
||||
printf("%s\n", line);
|
||||
fatal_exit("unhandled malloc");
|
||||
}
|
||||
if(sscanf(line+skip+7, "%ld", &num) != 1) {
|
||||
printf("%s\n%s\n", line, line+skip+7);
|
||||
fatal_exit("unhandled malloc");
|
||||
}
|
||||
cl = get_codeline(tree, codeline, name);
|
||||
if(!cl)
|
||||
fatal_exit("alloc failure");
|
||||
cl->alloc += num;
|
||||
cl->calls ++;
|
||||
}
|
||||
|
||||
/** read up the calloc stats */
|
||||
static void
|
||||
read_calloc_stat(char* line, rbtree_t* tree)
|
||||
{
|
||||
char codeline[10240];
|
||||
char name[10240];
|
||||
int skip = 0;
|
||||
long num = 0, sz = 0;
|
||||
struct codeline* cl = 0;
|
||||
line = strstr(line, "info: ")+6;
|
||||
if(sscanf(line, "%s %s %n", codeline, name, &skip) != 2) {
|
||||
printf("%s\n", line);
|
||||
fatal_exit("unhandled calloc");
|
||||
}
|
||||
if(sscanf(line+skip+7, "%ld, %ld", &num, &sz) != 2) {
|
||||
printf("%s\n%s\n", line, line+skip+7);
|
||||
fatal_exit("unhandled calloc");
|
||||
}
|
||||
|
||||
cl = get_codeline(tree, codeline, name);
|
||||
if(!cl)
|
||||
fatal_exit("alloc failure");
|
||||
cl->alloc += num*sz;
|
||||
cl->calls ++;
|
||||
}
|
||||
|
||||
/** get size of file */
|
||||
static off_t
|
||||
get_file_size(const char* fname)
|
||||
{
|
||||
struct stat s;
|
||||
if(stat(fname, &s) < 0) {
|
||||
fatal_exit("could not stat %s: %s", fname, strerror(errno));
|
||||
}
|
||||
return s.st_size;
|
||||
}
|
||||
|
||||
/** read the logfile */
|
||||
static void
|
||||
readfile(rbtree_t* tree, const char* fname)
|
||||
{
|
||||
off_t total = get_file_size(fname);
|
||||
off_t done = (off_t)0;
|
||||
int report = 0;
|
||||
FILE* in = fopen(fname, "r");
|
||||
char buf[102400];
|
||||
if(!in)
|
||||
fatal_exit("could not open %s: %s", fname, strerror(errno));
|
||||
printf("Reading %s of size " ARG_LL "d\n", fname, (long long)total);
|
||||
while(fgets(buf, 102400, in)) {
|
||||
buf[102400-1] = 0;
|
||||
done += (off_t)strlen(buf);
|
||||
/* progress count */
|
||||
if((int)(((double)done / (double)total)*100.) > report) {
|
||||
report = (int)(((double)done / (double)total)*100.);
|
||||
fprintf(stderr, " %d%%", report);
|
||||
}
|
||||
|
||||
if(!match(buf))
|
||||
continue;
|
||||
else if(strstr(buf+30, "malloc("))
|
||||
read_malloc_stat(buf, tree);
|
||||
else if(strstr(buf+30, "calloc("))
|
||||
read_calloc_stat(buf, tree);
|
||||
else {
|
||||
printf("%s\n", buf);
|
||||
fatal_exit("unhandled input");
|
||||
}
|
||||
}
|
||||
fprintf(stderr, " done\n");
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
/** print memory stats */
|
||||
static void
|
||||
printstats(rbtree_t* tree)
|
||||
{
|
||||
struct codeline* cl;
|
||||
uint64_t total = 0, tcalls = 0;
|
||||
RBTREE_FOR(cl, struct codeline*, tree) {
|
||||
printf("%12lld / %8lld in %s %s\n", (long long)cl->alloc,
|
||||
(long long)cl->calls, cl->codeline, cl->func);
|
||||
total += cl->alloc;
|
||||
tcalls += cl->calls;
|
||||
}
|
||||
printf("------------\n");
|
||||
printf("%12lld / %8lld total in %ld code lines\n", (long long)total,
|
||||
(long long)tcalls, (long)tree->count);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/** main program */
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
rbtree_t* tree = 0;
|
||||
if(argc != 2) {
|
||||
usage();
|
||||
}
|
||||
tree = rbtree_create(codeline_cmp);
|
||||
if(!tree)
|
||||
fatal_exit("alloc failure");
|
||||
readfile(tree, argv[1]);
|
||||
printstats(tree);
|
||||
return 0;
|
||||
}
|
||||
128
external/unbound/testcode/mini_tpkg.sh
vendored
Executable file
128
external/unbound/testcode/mini_tpkg.sh
vendored
Executable file
|
|
@ -0,0 +1,128 @@
|
|||
# tpkg that only exes the files.
|
||||
args="../.."
|
||||
if test "$1" = "-a"; then
|
||||
args=$2
|
||||
shift
|
||||
shift
|
||||
fi
|
||||
|
||||
if test "$1" = "clean"; then
|
||||
echo "rm -f result.* .done* .tpkg.var.master .tpkg.var.test"
|
||||
rm -f result.* .done* .tpkg.var.master .tpkg.var.test
|
||||
exit 0
|
||||
fi
|
||||
if test "$1" = "fake"; then
|
||||
echo "minitpkg fake $2"
|
||||
echo "fake" > .done-`basename $2 .tpkg`
|
||||
exit 0
|
||||
fi
|
||||
if test "$1" = "report" || test "$2" = "report"; then
|
||||
echo "Minitpkg Report"
|
||||
for result in *.tpkg; do
|
||||
name=`basename $result .tpkg`
|
||||
if test -f ".done-$name"; then
|
||||
if test "$1" != "-q"; then
|
||||
echo "** PASSED ** : $name"
|
||||
fi
|
||||
else
|
||||
if test -f "result.$name"; then
|
||||
echo "!! FAILED !! : $name"
|
||||
else
|
||||
echo ">> SKIPPED<< : $name"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test "$1" != 'exe'; then
|
||||
# usage
|
||||
echo "mini tpkg. Reduced functionality for old shells."
|
||||
echo " tpkg exe <file>"
|
||||
echo " tpkg fake <file>"
|
||||
echo " tpkg clean"
|
||||
echo " tpkg [-q] report"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
|
||||
# do not execute if the disk is too full
|
||||
#DISKLIMIT=100000
|
||||
# This check is not portable (to Solaris 10).
|
||||
#avail=`df . | tail -1 | awk '{print $4}'`
|
||||
#if test "$avail" -lt "$DISKLIMIT"; then
|
||||
#echo "minitpkg: The disk is too full! Only $avail."
|
||||
#exit 1
|
||||
#fi
|
||||
|
||||
name=`basename $1 .tpkg`
|
||||
dir=$name.$$
|
||||
result=result.$name
|
||||
done=.done-$name
|
||||
success="no"
|
||||
if test -x "`which bash`"; then
|
||||
shell="bash"
|
||||
else
|
||||
shell="sh"
|
||||
fi
|
||||
|
||||
# check already done
|
||||
if test -f .done-$name; then
|
||||
echo "minitpkg .done-$name exists. skip test."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Extract
|
||||
echo "minitpkg extract $1 to $dir"
|
||||
mkdir $dir
|
||||
gzip -cd $name.tpkg | (cd $dir; tar xf -)
|
||||
cd $dir
|
||||
mv $name.dir/* .
|
||||
|
||||
# EXE
|
||||
echo "minitpkg exe $name" > $result
|
||||
grep "Description:" $name.dsc >> $result 2>&1
|
||||
echo "DateRunStart: "`date "+%s" 2>/dev/null` >> $result
|
||||
if test -f $name.pre; then
|
||||
echo "minitpkg exe $name.pre"
|
||||
echo "minitpkg exe $name.pre" >> $result
|
||||
$shell $name.pre $args >> $result
|
||||
if test $? -ne 0; then
|
||||
echo "Warning: $name.pre did not exit successfully"
|
||||
fi
|
||||
fi
|
||||
if test -f $name.test; then
|
||||
echo "minitpkg exe $name.test"
|
||||
echo "minitpkg exe $name.test" >> $result
|
||||
$shell $name.test $args >>$result 2>&1
|
||||
if test $? -ne 0; then
|
||||
echo "$name: FAILED" >> $result
|
||||
echo "$name: FAILED"
|
||||
success="no"
|
||||
else
|
||||
echo "$name: PASSED" >> $result
|
||||
echo "$name: PASSED" > ../.done-$name
|
||||
echo "$name: PASSED"
|
||||
success="yes"
|
||||
fi
|
||||
fi
|
||||
if test -f $name.post; then
|
||||
echo "minitpkg exe $name.post"
|
||||
echo "minitpkg exe $name.post" >> $result
|
||||
$shell $name.post $args >> $result
|
||||
if test $? -ne 0; then
|
||||
echo "Warning: $name.post did not exit successfully"
|
||||
fi
|
||||
fi
|
||||
echo "DateRunEnd: "`date "+%s" 2>/dev/null` >> $result
|
||||
|
||||
mv $result ..
|
||||
cd ..
|
||||
rm -rf $dir
|
||||
# compat for windows where deletion may not succeed initially (files locked
|
||||
# by processes that still have to exit).
|
||||
if test $? -eq 1; then
|
||||
echo "minitpkg waiting for processes to terminate"
|
||||
sleep 2 # some time to exit, and try again
|
||||
rm -rf $dir
|
||||
fi
|
||||
653
external/unbound/testcode/perf.c
vendored
Normal file
653
external/unbound/testcode/perf.c
vendored
Normal file
|
|
@ -0,0 +1,653 @@
|
|||
/*
|
||||
* testcode/perf.c - debug program to estimate name server performance.
|
||||
*
|
||||
* Copyright (c) 2008, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This program estimates DNS name server performance.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
#include <signal.h>
|
||||
#include "util/log.h"
|
||||
#include "util/locks.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "ldns/sbuffer.h"
|
||||
#include "ldns/wire2str.h"
|
||||
#include "ldns/str2wire.h"
|
||||
#include <sys/time.h>
|
||||
|
||||
/** usage information for perf */
|
||||
static void usage(char* nm)
|
||||
{
|
||||
printf("usage: %s [options] server\n", nm);
|
||||
printf("server: ip address of server, IP4 or IP6.\n");
|
||||
printf(" If not on port %d add @port.\n", UNBOUND_DNS_PORT);
|
||||
printf("-d sec duration of test in whole seconds (0: wait for ^C)\n");
|
||||
printf("-a str query to ask, interpreted as a line from qfile\n");
|
||||
printf("-f fnm query list to read from file\n");
|
||||
printf(" every line has format: qname qclass qtype [+-]{E}\n");
|
||||
printf(" where + means RD set, E means EDNS enabled\n");
|
||||
printf("-q quiet mode, print only final qps\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct perfinfo;
|
||||
struct perfio;
|
||||
|
||||
/** Global info for perf */
|
||||
struct perfinfo {
|
||||
/** need to exit */
|
||||
volatile int exit;
|
||||
/** all purpose buffer (for UDP send and receive) */
|
||||
sldns_buffer* buf;
|
||||
|
||||
/** destination */
|
||||
struct sockaddr_storage dest;
|
||||
/** length of dest socket addr */
|
||||
socklen_t destlen;
|
||||
|
||||
/** when did this time slice start */
|
||||
struct timeval since;
|
||||
/** number of queries received in that time */
|
||||
size_t numrecv;
|
||||
/** number of queries sent out in that time */
|
||||
size_t numsent;
|
||||
|
||||
/** duration of test in seconds */
|
||||
int duration;
|
||||
/** quiet mode? */
|
||||
int quiet;
|
||||
|
||||
/** when did the total test start */
|
||||
struct timeval start;
|
||||
/** total number recvd */
|
||||
size_t total_recv;
|
||||
/** total number sent */
|
||||
size_t total_sent;
|
||||
/** numbers by rcode */
|
||||
size_t by_rcode[32];
|
||||
|
||||
/** number of I/O ports */
|
||||
size_t io_num;
|
||||
/** I/O ports array */
|
||||
struct perfio* io;
|
||||
/** max fd value in io ports */
|
||||
int maxfd;
|
||||
/** readset */
|
||||
fd_set rset;
|
||||
|
||||
/** size of querylist */
|
||||
size_t qlist_size;
|
||||
/** allocated size of qlist array */
|
||||
size_t qlist_capacity;
|
||||
/** list of query packets (data) */
|
||||
uint8_t** qlist_data;
|
||||
/** list of query packets (length of a packet) */
|
||||
size_t* qlist_len;
|
||||
/** index into querylist, for walking the list */
|
||||
size_t qlist_idx;
|
||||
};
|
||||
|
||||
/** I/O port for perf */
|
||||
struct perfio {
|
||||
/** id number */
|
||||
size_t id;
|
||||
/** file descriptor of socket */
|
||||
int fd;
|
||||
/** timeout value */
|
||||
struct timeval timeout;
|
||||
/** ptr back to perfinfo */
|
||||
struct perfinfo* info;
|
||||
};
|
||||
|
||||
/** number of msec between starting io ports */
|
||||
#define START_IO_INTERVAL 10
|
||||
/** number of msec timeout on io ports */
|
||||
#define IO_TIMEOUT 10
|
||||
|
||||
/** signal handler global info */
|
||||
static struct perfinfo* sig_info;
|
||||
|
||||
/** signal handler for user quit */
|
||||
static RETSIGTYPE perf_sigh(int sig)
|
||||
{
|
||||
log_assert(sig_info);
|
||||
if(!sig_info->quiet)
|
||||
printf("exit on signal %d\n", sig);
|
||||
sig_info->exit = 1;
|
||||
}
|
||||
|
||||
/** timeval compare, t1 < t2 */
|
||||
static int
|
||||
perf_tv_smaller(struct timeval* t1, struct timeval* t2)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
if(t1->tv_sec < t2->tv_sec)
|
||||
return 1;
|
||||
if(t1->tv_sec == t2->tv_sec &&
|
||||
t1->tv_usec < t2->tv_usec)
|
||||
return 1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** timeval add, t1 += t2 */
|
||||
static void
|
||||
perf_tv_add(struct timeval* t1, struct timeval* t2)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
t1->tv_sec += t2->tv_sec;
|
||||
t1->tv_usec += t2->tv_usec;
|
||||
while(t1->tv_usec > 1000000) {
|
||||
t1->tv_usec -= 1000000;
|
||||
t1->tv_sec++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** timeval subtract, t1 -= t2 */
|
||||
static void
|
||||
perf_tv_subtract(struct timeval* t1, struct timeval* t2)
|
||||
{
|
||||
#ifndef S_SPLINT_S
|
||||
t1->tv_sec -= t2->tv_sec;
|
||||
if(t1->tv_usec >= t2->tv_usec) {
|
||||
t1->tv_usec -= t2->tv_usec;
|
||||
} else {
|
||||
t1->tv_sec--;
|
||||
t1->tv_usec = 1000000-(t2->tv_usec-t1->tv_usec);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/** setup perf test environment */
|
||||
static void
|
||||
perfsetup(struct perfinfo* info)
|
||||
{
|
||||
size_t i;
|
||||
if(gettimeofday(&info->start, NULL) < 0)
|
||||
fatal_exit("gettimeofday: %s", strerror(errno));
|
||||
sig_info = info;
|
||||
if( signal(SIGINT, perf_sigh) == SIG_ERR ||
|
||||
#ifdef SIGQUIT
|
||||
signal(SIGQUIT, perf_sigh) == SIG_ERR ||
|
||||
#endif
|
||||
#ifdef SIGHUP
|
||||
signal(SIGHUP, perf_sigh) == SIG_ERR ||
|
||||
#endif
|
||||
#ifdef SIGBREAK
|
||||
signal(SIGBREAK, perf_sigh) == SIG_ERR ||
|
||||
#endif
|
||||
signal(SIGTERM, perf_sigh) == SIG_ERR)
|
||||
fatal_exit("could not bind to signal");
|
||||
info->io = (struct perfio*)calloc(sizeof(struct perfio), info->io_num);
|
||||
if(!info->io) fatal_exit("out of memory");
|
||||
#ifndef S_SPLINT_S
|
||||
FD_ZERO(&info->rset);
|
||||
#endif
|
||||
info->since = info->start;
|
||||
for(i=0; i<info->io_num; i++) {
|
||||
info->io[i].id = i;
|
||||
info->io[i].info = info;
|
||||
info->io[i].fd = socket(
|
||||
addr_is_ip6(&info->dest, info->destlen)?
|
||||
AF_INET6:AF_INET, SOCK_DGRAM, 0);
|
||||
if(info->io[i].fd == -1) {
|
||||
#ifndef USE_WINSOCK
|
||||
fatal_exit("socket: %s", strerror(errno));
|
||||
#else
|
||||
fatal_exit("socket: %s",
|
||||
wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
}
|
||||
if(info->io[i].fd > info->maxfd)
|
||||
info->maxfd = info->io[i].fd;
|
||||
#ifndef S_SPLINT_S
|
||||
FD_SET(FD_SET_T info->io[i].fd, &info->rset);
|
||||
info->io[i].timeout.tv_usec = ((START_IO_INTERVAL*i)%1000)
|
||||
*1000;
|
||||
info->io[i].timeout.tv_sec = (START_IO_INTERVAL*i)/1000;
|
||||
perf_tv_add(&info->io[i].timeout, &info->since);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/** cleanup perf test environment */
|
||||
static void
|
||||
perffree(struct perfinfo* info)
|
||||
{
|
||||
size_t i;
|
||||
if(!info) return;
|
||||
if(info->io) {
|
||||
for(i=0; i<info->io_num; i++) {
|
||||
#ifndef USE_WINSOCK
|
||||
close(info->io[i].fd);
|
||||
#else
|
||||
closesocket(info->io[i].fd);
|
||||
#endif
|
||||
}
|
||||
free(info->io);
|
||||
}
|
||||
for(i=0; i<info->qlist_size; i++)
|
||||
free(info->qlist_data[i]);
|
||||
free(info->qlist_data);
|
||||
free(info->qlist_len);
|
||||
}
|
||||
|
||||
/** send new query for io */
|
||||
static void
|
||||
perfsend(struct perfinfo* info, size_t n, struct timeval* now)
|
||||
{
|
||||
ssize_t r;
|
||||
r = sendto(info->io[n].fd, (void*)info->qlist_data[info->qlist_idx],
|
||||
info->qlist_len[info->qlist_idx], 0,
|
||||
(struct sockaddr*)&info->dest, info->destlen);
|
||||
/*log_hex("send", info->qlist_data[info->qlist_idx],
|
||||
info->qlist_len[info->qlist_idx]);*/
|
||||
if(r == -1) {
|
||||
#ifndef USE_WINSOCK
|
||||
log_err("sendto: %s", strerror(errno));
|
||||
#else
|
||||
log_err("sendto: %s", wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
} else if(r != (ssize_t)info->qlist_len[info->qlist_idx]) {
|
||||
log_err("partial sendto");
|
||||
}
|
||||
info->qlist_idx = (info->qlist_idx+1) % info->qlist_size;
|
||||
info->numsent++;
|
||||
|
||||
info->io[n].timeout.tv_sec = IO_TIMEOUT/1000;
|
||||
info->io[n].timeout.tv_usec = (IO_TIMEOUT%1000)*1000;
|
||||
perf_tv_add(&info->io[n].timeout, now);
|
||||
}
|
||||
|
||||
/** got reply for io */
|
||||
static void
|
||||
perfreply(struct perfinfo* info, size_t n, struct timeval* now)
|
||||
{
|
||||
ssize_t r;
|
||||
r = recv(info->io[n].fd, (void*)sldns_buffer_begin(info->buf),
|
||||
sldns_buffer_capacity(info->buf), 0);
|
||||
if(r == -1) {
|
||||
#ifndef USE_WINSOCK
|
||||
log_err("recv: %s", strerror(errno));
|
||||
#else
|
||||
log_err("recv: %s", wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
} else {
|
||||
info->by_rcode[LDNS_RCODE_WIRE(sldns_buffer_begin(
|
||||
info->buf))]++;
|
||||
info->numrecv++;
|
||||
}
|
||||
/*sldns_buffer_set_limit(info->buf, r);
|
||||
log_buf(0, "reply", info->buf);*/
|
||||
perfsend(info, n, now);
|
||||
}
|
||||
|
||||
/** got timeout for io */
|
||||
static void
|
||||
perftimeout(struct perfinfo* info, size_t n, struct timeval* now)
|
||||
{
|
||||
/* may not be a dropped packet, this is also used to start
|
||||
* up the sending IOs */
|
||||
perfsend(info, n, now);
|
||||
}
|
||||
|
||||
/** print nice stats about qps */
|
||||
static void
|
||||
stat_printout(struct perfinfo* info, struct timeval* now,
|
||||
struct timeval* elapsed)
|
||||
{
|
||||
/* calculate qps */
|
||||
double dt, qps = 0;
|
||||
#ifndef S_SPLINT_S
|
||||
dt = (double)(elapsed->tv_sec*1000000 + elapsed->tv_usec) / 1000000;
|
||||
#endif
|
||||
if(dt > 0.001)
|
||||
qps = (double)(info->numrecv) / dt;
|
||||
if(!info->quiet)
|
||||
printf("qps: %g\n", qps);
|
||||
/* setup next slice */
|
||||
info->since = *now;
|
||||
info->total_sent += info->numsent;
|
||||
info->total_recv += info->numrecv;
|
||||
info->numrecv = 0;
|
||||
info->numsent = 0;
|
||||
}
|
||||
|
||||
/** wait for new events for performance test */
|
||||
static void
|
||||
perfselect(struct perfinfo* info)
|
||||
{
|
||||
fd_set rset = info->rset;
|
||||
struct timeval timeout, now;
|
||||
int num;
|
||||
size_t i;
|
||||
if(gettimeofday(&now, NULL) < 0)
|
||||
fatal_exit("gettimeofday: %s", strerror(errno));
|
||||
/* time to exit? */
|
||||
if(info->duration > 0) {
|
||||
timeout = now;
|
||||
perf_tv_subtract(&timeout, &info->start);
|
||||
if((int)timeout.tv_sec >= info->duration) {
|
||||
info->exit = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* time for stats printout? */
|
||||
timeout = now;
|
||||
perf_tv_subtract(&timeout, &info->since);
|
||||
if(timeout.tv_sec > 0) {
|
||||
stat_printout(info, &now, &timeout);
|
||||
}
|
||||
/* see what is closest port to timeout; or if there is a timeout */
|
||||
timeout = info->io[0].timeout;
|
||||
for(i=0; i<info->io_num; i++) {
|
||||
if(perf_tv_smaller(&info->io[i].timeout, &now)) {
|
||||
perftimeout(info, i, &now);
|
||||
return;
|
||||
}
|
||||
if(perf_tv_smaller(&info->io[i].timeout, &timeout)) {
|
||||
timeout = info->io[i].timeout;
|
||||
}
|
||||
}
|
||||
perf_tv_subtract(&timeout, &now);
|
||||
|
||||
num = select(info->maxfd+1, &rset, NULL, NULL, &timeout);
|
||||
if(num == -1) {
|
||||
if(errno == EAGAIN || errno == EINTR)
|
||||
return;
|
||||
log_err("select: %s", strerror(errno));
|
||||
}
|
||||
|
||||
/* handle new events */
|
||||
for(i=0; num && i<info->io_num; i++) {
|
||||
if(FD_ISSET(info->io[i].fd, &rset)) {
|
||||
perfreply(info, i, &now);
|
||||
num--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** show end stats */
|
||||
static void
|
||||
perfendstats(struct perfinfo* info)
|
||||
{
|
||||
double dt, qps;
|
||||
struct timeval timeout, now;
|
||||
int i, lost;
|
||||
if(gettimeofday(&now, NULL) < 0)
|
||||
fatal_exit("gettimeofday: %s", strerror(errno));
|
||||
timeout = now;
|
||||
perf_tv_subtract(&timeout, &info->since);
|
||||
stat_printout(info, &now, &timeout);
|
||||
|
||||
timeout = now;
|
||||
perf_tv_subtract(&timeout, &info->start);
|
||||
dt = (double)(timeout.tv_sec*1000000 + timeout.tv_usec) / 1000000.0;
|
||||
qps = (double)(info->total_recv) / dt;
|
||||
lost = (int)(info->total_sent - info->total_recv) - (int)info->io_num;
|
||||
if(!info->quiet) {
|
||||
printf("overall time: %g sec\n",
|
||||
(double)timeout.tv_sec +
|
||||
(double)timeout.tv_usec/1000000.);
|
||||
if(lost > 0)
|
||||
printf("Packets lost: %d\n", (int)lost);
|
||||
|
||||
for(i=0; i<(int)(sizeof(info->by_rcode)/sizeof(size_t)); i++)
|
||||
{
|
||||
if(info->by_rcode[i] > 0) {
|
||||
char rc[16];
|
||||
sldns_wire2str_rcode_buf(i, rc, sizeof(rc));
|
||||
printf("%d(%5s): %u replies\n",
|
||||
i, rc, (unsigned)info->by_rcode[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("average qps: %g\n", qps);
|
||||
}
|
||||
|
||||
/** perform the performance test */
|
||||
static void
|
||||
perfmain(struct perfinfo* info)
|
||||
{
|
||||
perfsetup(info);
|
||||
while(!info->exit) {
|
||||
perfselect(info);
|
||||
}
|
||||
perfendstats(info);
|
||||
perffree(info);
|
||||
}
|
||||
|
||||
/** parse a query line to a packet into buffer */
|
||||
static int
|
||||
qlist_parse_line(sldns_buffer* buf, char* p)
|
||||
{
|
||||
char nm[1024], cl[1024], tp[1024], fl[1024];
|
||||
int r;
|
||||
int rec = 1, edns = 0;
|
||||
struct query_info qinfo;
|
||||
nm[0] = 0; cl[0] = 0; tp[0] = 0; fl[0] = 0;
|
||||
r = sscanf(p, " %1023s %1023s %1023s %1023s", nm, cl, tp, fl);
|
||||
if(r != 3 && r != 4)
|
||||
return 0;
|
||||
/*printf("nm='%s', cl='%s', tp='%s', fl='%s'\n", nm, cl, tp, fl);*/
|
||||
if(strcmp(tp, "IN") == 0 || strcmp(tp, "CH") == 0) {
|
||||
qinfo.qtype = sldns_get_rr_type_by_name(cl);
|
||||
qinfo.qclass = sldns_get_rr_class_by_name(tp);
|
||||
} else {
|
||||
qinfo.qtype = sldns_get_rr_type_by_name(tp);
|
||||
qinfo.qclass = sldns_get_rr_class_by_name(cl);
|
||||
}
|
||||
if(fl[0] == '+') rec = 1;
|
||||
else if(fl[0] == '-') rec = 0;
|
||||
else if(fl[0] == 'E') edns = 1;
|
||||
if((fl[0] == '+' || fl[0] == '-') && fl[1] == 'E')
|
||||
edns = 1;
|
||||
qinfo.qname = sldns_str2wire_dname(nm, &qinfo.qname_len);
|
||||
if(!qinfo.qname)
|
||||
return 0;
|
||||
qinfo_query_encode(buf, &qinfo);
|
||||
sldns_buffer_write_u16_at(buf, 0, 0); /* zero ID */
|
||||
if(rec) LDNS_RD_SET(sldns_buffer_begin(buf));
|
||||
if(edns) {
|
||||
struct edns_data ed;
|
||||
memset(&ed, 0, sizeof(ed));
|
||||
ed.edns_present = 1;
|
||||
ed.udp_size = EDNS_ADVERTISED_SIZE;
|
||||
/* Set DO bit in all EDNS datagrams ... */
|
||||
ed.bits = EDNS_DO;
|
||||
attach_edns_record(buf, &ed);
|
||||
}
|
||||
free(qinfo.qname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** grow query list capacity */
|
||||
static void
|
||||
qlist_grow_capacity(struct perfinfo* info)
|
||||
{
|
||||
size_t newcap = (size_t)((info->qlist_capacity==0)?16:
|
||||
info->qlist_capacity*2);
|
||||
uint8_t** d = (uint8_t**)calloc(sizeof(uint8_t*), newcap);
|
||||
size_t* l = (size_t*)calloc(sizeof(size_t), newcap);
|
||||
if(!d || !l) fatal_exit("out of memory");
|
||||
memcpy(d, info->qlist_data, sizeof(uint8_t*)*
|
||||
info->qlist_capacity);
|
||||
memcpy(l, info->qlist_len, sizeof(size_t)*
|
||||
info->qlist_capacity);
|
||||
free(info->qlist_data);
|
||||
free(info->qlist_len);
|
||||
info->qlist_data = d;
|
||||
info->qlist_len = l;
|
||||
info->qlist_capacity = newcap;
|
||||
}
|
||||
|
||||
/** setup query list in info */
|
||||
static void
|
||||
qlist_add_line(struct perfinfo* info, char* line, int no)
|
||||
{
|
||||
if(!qlist_parse_line(info->buf, line)) {
|
||||
printf("error parsing query %d: %s\n", no, line);
|
||||
exit(1);
|
||||
}
|
||||
sldns_buffer_write_u16_at(info->buf, 0, (uint16_t)info->qlist_size);
|
||||
if(info->qlist_size + 1 > info->qlist_capacity) {
|
||||
qlist_grow_capacity(info);
|
||||
}
|
||||
info->qlist_len[info->qlist_size] = sldns_buffer_limit(info->buf);
|
||||
info->qlist_data[info->qlist_size] = memdup(
|
||||
sldns_buffer_begin(info->buf), sldns_buffer_limit(info->buf));
|
||||
if(!info->qlist_data[info->qlist_size])
|
||||
fatal_exit("out of memory");
|
||||
info->qlist_size ++;
|
||||
}
|
||||
|
||||
/** setup query list in info */
|
||||
static void
|
||||
qlist_read_file(struct perfinfo* info, char* fname)
|
||||
{
|
||||
char buf[1024];
|
||||
char *p;
|
||||
FILE* in = fopen(fname, "r");
|
||||
int lineno = 0;
|
||||
if(!in) {
|
||||
perror(fname);
|
||||
exit(1);
|
||||
}
|
||||
while(fgets(buf, (int)sizeof(buf), in)) {
|
||||
lineno++;
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
p = buf;
|
||||
while(*p == ' ' || *p == '\t')
|
||||
p++;
|
||||
if(p[0] == 0 || p[0] == '\n' || p[0] == ';' || p[0] == '#')
|
||||
continue;
|
||||
qlist_add_line(info, p, lineno);
|
||||
}
|
||||
printf("Read %s, got %u queries\n", fname, (unsigned)info->qlist_size);
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern int optind;
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern char* optarg;
|
||||
|
||||
/** main program for perf */
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
char* nm = argv[0];
|
||||
int c;
|
||||
struct perfinfo info;
|
||||
#ifdef USE_WINSOCK
|
||||
int r;
|
||||
WSADATA wsa_data;
|
||||
#endif
|
||||
|
||||
/* defaults */
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.io_num = 16;
|
||||
|
||||
log_init(NULL, 0, NULL);
|
||||
log_ident_set("perf");
|
||||
checklock_start();
|
||||
#ifdef USE_WINSOCK
|
||||
if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0)
|
||||
fatal_exit("WSAStartup failed: %s", wsa_strerror(r));
|
||||
#endif
|
||||
|
||||
info.buf = sldns_buffer_new(65553);
|
||||
if(!info.buf) fatal_exit("out of memory");
|
||||
|
||||
/* parse the options */
|
||||
while( (c=getopt(argc, argv, "d:ha:f:q")) != -1) {
|
||||
switch(c) {
|
||||
case 'q':
|
||||
info.quiet = 1;
|
||||
break;
|
||||
case 'd':
|
||||
if(atoi(optarg)==0 && strcmp(optarg, "0")!=0) {
|
||||
printf("-d not a number %s", optarg);
|
||||
return 1;
|
||||
}
|
||||
info.duration = atoi(optarg);
|
||||
break;
|
||||
case 'a':
|
||||
qlist_add_line(&info, optarg, 0);
|
||||
break;
|
||||
case 'f':
|
||||
qlist_read_file(&info, optarg);
|
||||
break;
|
||||
case '?':
|
||||
case 'h':
|
||||
default:
|
||||
usage(nm);
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if(argc != 1) {
|
||||
printf("error: pass server IP address on commandline.\n");
|
||||
usage(nm);
|
||||
}
|
||||
if(!extstrtoaddr(argv[0], &info.dest, &info.destlen)) {
|
||||
printf("Could not parse ip: %s\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
if(info.qlist_size == 0) {
|
||||
printf("No queries to make, use -f or -a.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* do the performance test */
|
||||
perfmain(&info);
|
||||
|
||||
sldns_buffer_free(info.buf);
|
||||
#ifdef USE_WINSOCK
|
||||
WSACleanup();
|
||||
#endif
|
||||
checklock_stop();
|
||||
return 0;
|
||||
}
|
||||
632
external/unbound/testcode/petal.c
vendored
Normal file
632
external/unbound/testcode/petal.c
vendored
Normal file
|
|
@ -0,0 +1,632 @@
|
|||
/*
|
||||
* petal.c - https daemon that is small and beautiful.
|
||||
*
|
||||
* Copyright (c) 2010, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* HTTP1.1/SSL server.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
#ifdef HAVE_OPENSSL_SSL_H
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
#ifdef HAVE_OPENSSL_ERR_H
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
#ifdef HAVE_OPENSSL_RAND_H
|
||||
#include <openssl/rand.h>
|
||||
#endif
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#if defined(UNBOUND_ALLOC_LITE) || defined(UNBOUND_ALLOC_STATS)
|
||||
#ifdef malloc
|
||||
#undef malloc
|
||||
#endif
|
||||
#ifdef free
|
||||
#undef free
|
||||
#endif
|
||||
#endif /* alloc lite or alloc stats */
|
||||
|
||||
/** verbosity for this application */
|
||||
static int verb = 0;
|
||||
|
||||
/** Give petal usage, and exit (1). */
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
printf("Usage: petal [opts]\n");
|
||||
printf(" https daemon serves files from ./'host'/filename\n");
|
||||
printf(" (no hostname: from the 'default' directory)\n");
|
||||
printf("-a addr bind to this address, 127.0.0.1\n");
|
||||
printf("-p port port number, default 443\n");
|
||||
printf("-k keyfile SSL private key file (PEM), petal.key\n");
|
||||
printf("-c certfile SSL certificate file (PEM), petal.pem\n");
|
||||
printf("-v more verbose\n");
|
||||
printf("-h show this usage help\n");
|
||||
printf("Version %s\n", PACKAGE_VERSION);
|
||||
printf("BSD licensed, see LICENSE in source package for details.\n");
|
||||
printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** fatal exit */
|
||||
static void print_exit(const char* str) {printf("error %s\n", str); exit(1);}
|
||||
/** print errno */
|
||||
static void log_errno(const char* str)
|
||||
{printf("error %s: %s\n", str, strerror(errno));}
|
||||
|
||||
/** parse a text IP address into a sockaddr */
|
||||
static int
|
||||
parse_ip_addr(char* str, int port, struct sockaddr_storage* ret, socklen_t* l)
|
||||
{
|
||||
socklen_t len = 0;
|
||||
struct sockaddr_storage* addr = NULL;
|
||||
struct sockaddr_in6 a6;
|
||||
struct sockaddr_in a;
|
||||
uint16_t p = (uint16_t)port;
|
||||
int fam = 0;
|
||||
memset(&a6, 0, sizeof(a6));
|
||||
memset(&a, 0, sizeof(a));
|
||||
|
||||
if(inet_pton(AF_INET6, str, &a6.sin6_addr) > 0) {
|
||||
/* it is an IPv6 */
|
||||
fam = AF_INET6;
|
||||
a6.sin6_family = AF_INET6;
|
||||
a6.sin6_port = (in_port_t)htons(p);
|
||||
addr = (struct sockaddr_storage*)&a6;
|
||||
len = (socklen_t)sizeof(struct sockaddr_in6);
|
||||
}
|
||||
if(inet_pton(AF_INET, str, &a.sin_addr) > 0) {
|
||||
/* it is an IPv4 */
|
||||
fam = AF_INET;
|
||||
a.sin_family = AF_INET;
|
||||
a.sin_port = (in_port_t)htons(p);
|
||||
addr = (struct sockaddr_storage*)&a;
|
||||
len = (socklen_t)sizeof(struct sockaddr_in);
|
||||
}
|
||||
if(!len) print_exit("cannot parse addr");
|
||||
*l = len;
|
||||
memmove(ret, addr, len);
|
||||
return fam;
|
||||
}
|
||||
|
||||
/** close the fd */
|
||||
static void
|
||||
fd_close(int fd)
|
||||
{
|
||||
#ifndef USE_WINSOCK
|
||||
close(fd);
|
||||
#else
|
||||
closesocket(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one line from SSL
|
||||
* zero terminates.
|
||||
* skips "\r\n" (but not copied to buf).
|
||||
* @param ssl: the SSL connection to read from (blocking).
|
||||
* @param buf: buffer to return line in.
|
||||
* @param len: size of the buffer.
|
||||
* @return 0 on error, 1 on success.
|
||||
*/
|
||||
static int
|
||||
read_ssl_line(SSL* ssl, char* buf, size_t len)
|
||||
{
|
||||
size_t n = 0;
|
||||
int r;
|
||||
int endnl = 0;
|
||||
while(1) {
|
||||
if(n >= len) {
|
||||
if(verb) printf("line too long\n");
|
||||
return 0;
|
||||
}
|
||||
if((r = SSL_read(ssl, buf+n, 1)) <= 0) {
|
||||
if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) {
|
||||
/* EOF */
|
||||
break;
|
||||
}
|
||||
if(verb) printf("could not SSL_read\n");
|
||||
return 0;
|
||||
}
|
||||
if(endnl && buf[n] == '\n') {
|
||||
break;
|
||||
} else if(endnl) {
|
||||
/* bad data */
|
||||
if(verb) printf("error: stray linefeeds\n");
|
||||
return 0;
|
||||
} else if(buf[n] == '\r') {
|
||||
/* skip \r, and also \n on the wire */
|
||||
endnl = 1;
|
||||
continue;
|
||||
} else if(buf[n] == '\n') {
|
||||
/* skip the \n, we are done */
|
||||
break;
|
||||
} else n++;
|
||||
}
|
||||
buf[n] = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** process one http header */
|
||||
static int
|
||||
process_one_header(char* buf, char* file, size_t flen, char* host, size_t hlen,
|
||||
int* vs)
|
||||
{
|
||||
if(strncasecmp(buf, "GET ", 4) == 0) {
|
||||
char* e = strstr(buf, " HTTP/1.1");
|
||||
if(!e) e = strstr(buf, " http/1.1");
|
||||
if(!e) {
|
||||
e = strstr(buf, " HTTP/1.0");
|
||||
if(!e) e = strstr(buf, " http/1.0");
|
||||
if(!e) e = strrchr(buf, ' ');
|
||||
if(!e) e = strrchr(buf, '\t');
|
||||
if(e) *vs = 10;
|
||||
}
|
||||
if(e) *e = 0;
|
||||
if(strlen(buf) < 4) return 0;
|
||||
(void)strlcpy(file, buf+4, flen);
|
||||
} else if(strncasecmp(buf, "Host: ", 6) == 0) {
|
||||
(void)strlcpy(host, buf+6, hlen);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** read http headers and process them */
|
||||
static int
|
||||
read_http_headers(SSL* ssl, char* file, size_t flen, char* host, size_t hlen,
|
||||
int* vs)
|
||||
{
|
||||
char buf[1024];
|
||||
file[0] = 0;
|
||||
host[0] = 0;
|
||||
while(read_ssl_line(ssl, buf, sizeof(buf))) {
|
||||
if(verb>=2) printf("read: %s\n", buf);
|
||||
if(buf[0] == 0)
|
||||
return 1;
|
||||
if(!process_one_header(buf, file, flen, host, hlen, vs))
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** setup SSL context */
|
||||
static SSL_CTX*
|
||||
setup_ctx(char* key, char* cert)
|
||||
{
|
||||
SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
if(!ctx) print_exit("out of memory");
|
||||
(void)SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
|
||||
if(!SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM))
|
||||
print_exit("cannot read cert");
|
||||
if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM))
|
||||
print_exit("cannot read key");
|
||||
if(!SSL_CTX_check_private_key(ctx))
|
||||
print_exit("private key is not correct");
|
||||
if(!SSL_CTX_load_verify_locations(ctx, cert, NULL))
|
||||
print_exit("cannot load cert verify locations");
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/** setup listening TCP */
|
||||
static int
|
||||
setup_fd(char* addr, int port)
|
||||
{
|
||||
struct sockaddr_storage ad;
|
||||
socklen_t len;
|
||||
int fd;
|
||||
int c = 1;
|
||||
int fam = parse_ip_addr(addr, port, &ad, &len);
|
||||
fd = socket(fam, SOCK_STREAM, 0);
|
||||
if(fd == -1) {
|
||||
log_errno("socket");
|
||||
return -1;
|
||||
}
|
||||
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
|
||||
(void*)&c, (socklen_t) sizeof(int)) < 0) {
|
||||
log_errno("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
|
||||
}
|
||||
if(bind(fd, (struct sockaddr*)&ad, len) == -1) {
|
||||
log_errno("bind");
|
||||
fd_close(fd);
|
||||
return -1;
|
||||
}
|
||||
if(listen(fd, 5) == -1) {
|
||||
log_errno("listen");
|
||||
fd_close(fd);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/** setup SSL connection to the client */
|
||||
static SSL*
|
||||
setup_ssl(int s, SSL_CTX* ctx)
|
||||
{
|
||||
SSL* ssl = SSL_new(ctx);
|
||||
if(!ssl) return NULL;
|
||||
SSL_set_accept_state(ssl);
|
||||
(void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
|
||||
if(!SSL_set_fd(ssl, s)) {
|
||||
SSL_free(ssl);
|
||||
return NULL;
|
||||
}
|
||||
return ssl;
|
||||
}
|
||||
|
||||
/** check a file name for safety */
|
||||
static int
|
||||
file_name_is_safe(char* s)
|
||||
{
|
||||
size_t l = strlen(s);
|
||||
if(s[0] != '/')
|
||||
return 0; /* must start with / */
|
||||
if(strstr(s, "/../"))
|
||||
return 0; /* no updirs in URL */
|
||||
if(l>=3 && s[l-1]=='.' && s[l-2]=='.' && s[l-3]=='/')
|
||||
return 0; /* ends with /.. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** adjust host and filename */
|
||||
static void
|
||||
adjust_host_file(char* host, char* file)
|
||||
{
|
||||
size_t i, len;
|
||||
/* remove a port number if present */
|
||||
if(strrchr(host, ':'))
|
||||
*strrchr(host, ':') = 0;
|
||||
/* lowercase */
|
||||
len = strlen(host);
|
||||
for(i=0; i<len; i++)
|
||||
host[i] = tolower((unsigned char)host[i]);
|
||||
len = strlen(file);
|
||||
for(i=0; i<len; i++)
|
||||
file[i] = tolower((unsigned char)file[i]);
|
||||
}
|
||||
|
||||
/** check a host name for safety */
|
||||
static int
|
||||
host_name_is_safe(char* s)
|
||||
{
|
||||
if(strchr(s, '/'))
|
||||
return 0;
|
||||
if(strcmp(s, "..") == 0)
|
||||
return 0;
|
||||
if(strcmp(s, ".") == 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** provide file in whole transfer */
|
||||
static void
|
||||
provide_file_10(SSL* ssl, char* fname)
|
||||
{
|
||||
char* buf, *at;
|
||||
size_t len, avail, header_reserve=1024;
|
||||
FILE* in = fopen(fname,
|
||||
#ifndef USE_WINSOCK
|
||||
"r"
|
||||
#else
|
||||
"rb"
|
||||
#endif
|
||||
);
|
||||
size_t r;
|
||||
const char* rcode = "200 OK";
|
||||
if(!in) {
|
||||
char hdr[1024];
|
||||
rcode = "404 File not found";
|
||||
snprintf(hdr, sizeof(hdr), "HTTP/1.1 %s\r\n\r\n", rcode);
|
||||
r = strlen(hdr);
|
||||
if(SSL_write(ssl, hdr, (int)r) <= 0) {
|
||||
/* write failure */
|
||||
}
|
||||
return;
|
||||
}
|
||||
fseek(in, 0, SEEK_END);
|
||||
len = (size_t)ftell(in);
|
||||
fseek(in, 0, SEEK_SET);
|
||||
/* plus some space for the header */
|
||||
buf = (char*)malloc(len+header_reserve);
|
||||
if(!buf) {
|
||||
fclose(in);
|
||||
return;
|
||||
}
|
||||
avail = len+header_reserve;
|
||||
at = buf;
|
||||
snprintf(at, avail, "HTTP/1.1 %s\r\n", rcode);
|
||||
r = strlen(at);
|
||||
at += r;
|
||||
avail -= r;
|
||||
snprintf(at, avail, "Server: petal/%s\r\n", PACKAGE_VERSION);
|
||||
r = strlen(at);
|
||||
at += r;
|
||||
avail -= r;
|
||||
snprintf(at, avail, "Content-Length: %u\r\n", (unsigned)len);
|
||||
r = strlen(at);
|
||||
at += r;
|
||||
avail -= r;
|
||||
snprintf(at, avail, "\r\n");
|
||||
r = strlen(at);
|
||||
at += r;
|
||||
avail -= r;
|
||||
if(avail < len) { /* robust */
|
||||
free(buf);
|
||||
fclose(in);
|
||||
return;
|
||||
}
|
||||
if(fread(at, 1, len, in) != len) {
|
||||
free(buf);
|
||||
fclose(in);
|
||||
return;
|
||||
}
|
||||
fclose(in);
|
||||
at += len;
|
||||
avail -= len;
|
||||
if(SSL_write(ssl, buf, at-buf) <= 0) {
|
||||
/* write failure */
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/** provide file over SSL, chunked encoding */
|
||||
static void
|
||||
provide_file_chunked(SSL* ssl, char* fname)
|
||||
{
|
||||
char buf[16384];
|
||||
char* at = buf;
|
||||
size_t avail = sizeof(buf);
|
||||
size_t r;
|
||||
FILE* in = fopen(fname,
|
||||
#ifndef USE_WINSOCK
|
||||
"r"
|
||||
#else
|
||||
"rb"
|
||||
#endif
|
||||
);
|
||||
const char* rcode = "200 OK";
|
||||
if(!in) {
|
||||
rcode = "404 File not found";
|
||||
}
|
||||
|
||||
/* print headers */
|
||||
snprintf(at, avail, "HTTP/1.1 %s\r\n", rcode);
|
||||
r = strlen(at);
|
||||
at += r;
|
||||
avail -= r;
|
||||
snprintf(at, avail, "Server: petal/%s\r\n", PACKAGE_VERSION);
|
||||
r = strlen(at);
|
||||
at += r;
|
||||
avail -= r;
|
||||
snprintf(at, avail, "Transfer-Encoding: chunked\r\n");
|
||||
r = strlen(at);
|
||||
at += r;
|
||||
avail -= r;
|
||||
snprintf(at, avail, "Connection: close\r\n");
|
||||
r = strlen(at);
|
||||
at += r;
|
||||
avail -= r;
|
||||
snprintf(at, avail, "\r\n");
|
||||
r = strlen(at);
|
||||
at += r;
|
||||
avail -= r;
|
||||
if(avail < 16) { /* robust */
|
||||
if(in) fclose(in);
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
char tmpbuf[sizeof(buf)];
|
||||
/* read chunk; space-16 for xxxxCRLF..CRLF0CRLFCRLF (3 spare)*/
|
||||
size_t red = in?fread(tmpbuf, 1, avail-16, in):0;
|
||||
/* prepare chunk */
|
||||
snprintf(at, avail, "%x\r\n", (unsigned)red);
|
||||
r = strlen(at);
|
||||
if(verb >= 3)
|
||||
{printf("chunk len %x\n", (unsigned)red); fflush(stdout);}
|
||||
at += r;
|
||||
avail -= r;
|
||||
if(red != 0) {
|
||||
if(red > avail) break; /* robust */
|
||||
memmove(at, tmpbuf, red);
|
||||
at += red;
|
||||
avail -= red;
|
||||
snprintf(at, avail, "\r\n");
|
||||
r = strlen(at);
|
||||
at += r;
|
||||
avail -= r;
|
||||
}
|
||||
if(in && feof(in) && red != 0) {
|
||||
snprintf(at, avail, "0\r\n");
|
||||
r = strlen(at);
|
||||
at += r;
|
||||
avail -= r;
|
||||
}
|
||||
if(!in || feof(in)) {
|
||||
snprintf(at, avail, "\r\n");
|
||||
r = strlen(at);
|
||||
at += r;
|
||||
avail -= r;
|
||||
}
|
||||
/* send chunk */
|
||||
if(SSL_write(ssl, buf, at-buf) <= 0) {
|
||||
/* SSL error */
|
||||
break;
|
||||
}
|
||||
|
||||
/* setup for next chunk */
|
||||
at = buf;
|
||||
avail = sizeof(buf);
|
||||
} while(in && !feof(in) && !ferror(in));
|
||||
|
||||
if(in) fclose(in);
|
||||
}
|
||||
|
||||
/** provide service to the ssl descriptor */
|
||||
static void
|
||||
service_ssl(SSL* ssl, struct sockaddr_storage* from, socklen_t falen)
|
||||
{
|
||||
char file[1024];
|
||||
char host[1024];
|
||||
char combined[2048];
|
||||
int vs = 11;
|
||||
if(!read_http_headers(ssl, file, sizeof(file), host, sizeof(host),
|
||||
&vs))
|
||||
return;
|
||||
adjust_host_file(host, file);
|
||||
if(host[0] == 0 || !host_name_is_safe(host))
|
||||
(void)strlcpy(host, "default", sizeof(host));
|
||||
if(!file_name_is_safe(file)) {
|
||||
return;
|
||||
}
|
||||
snprintf(combined, sizeof(combined), "%s%s", host, file);
|
||||
if(verb) {
|
||||
char out[100];
|
||||
void* a = &((struct sockaddr_in*)from)->sin_addr;
|
||||
if(falen != (socklen_t)sizeof(struct sockaddr_in))
|
||||
a = &((struct sockaddr_in6*)from)->sin6_addr;
|
||||
out[0]=0;
|
||||
(void)inet_ntop((int)((struct sockaddr_in*)from)->sin_family,
|
||||
a, out, (socklen_t)sizeof(out));
|
||||
printf("%s requests %s\n", out, combined);
|
||||
fflush(stdout);
|
||||
}
|
||||
if(vs == 10)
|
||||
provide_file_10(ssl, combined);
|
||||
else provide_file_chunked(ssl, combined);
|
||||
}
|
||||
|
||||
/** provide ssl service */
|
||||
static void
|
||||
do_service(char* addr, int port, char* key, char* cert)
|
||||
{
|
||||
SSL_CTX* sslctx = setup_ctx(key, cert);
|
||||
int fd = setup_fd(addr, port);
|
||||
int go = 1;
|
||||
if(fd == -1) print_exit("could not setup sockets");
|
||||
if(verb) {printf("petal start\n"); fflush(stdout);}
|
||||
while(go) {
|
||||
struct sockaddr_storage from;
|
||||
socklen_t flen = (socklen_t)sizeof(from);
|
||||
int s = accept(fd, (struct sockaddr*)&from, &flen);
|
||||
if(verb) fflush(stdout);
|
||||
if(s != -1) {
|
||||
SSL* ssl = setup_ssl(s, sslctx);
|
||||
if(verb) fflush(stdout);
|
||||
if(ssl) {
|
||||
service_ssl(ssl, &from, flen);
|
||||
if(verb) fflush(stdout);
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
}
|
||||
fd_close(s);
|
||||
} else if (verb >=2) log_errno("accept");
|
||||
if(verb) fflush(stdout);
|
||||
}
|
||||
/* if we get a kill signal, the process dies and the OS reaps us */
|
||||
if(verb) printf("petal end\n");
|
||||
fd_close(fd);
|
||||
SSL_CTX_free(sslctx);
|
||||
}
|
||||
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern int optind;
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern char* optarg;
|
||||
|
||||
/** Main routine for petal */
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int c;
|
||||
int port = 443;
|
||||
char* addr = "127.0.0.1", *key = "petal.key", *cert = "petal.pem";
|
||||
#ifdef USE_WINSOCK
|
||||
WSADATA wsa_data;
|
||||
if((c=WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0)
|
||||
{ printf("WSAStartup failed\n"); exit(1); }
|
||||
atexit((void (*)(void))WSACleanup);
|
||||
#endif
|
||||
|
||||
/* parse the options */
|
||||
while( (c=getopt(argc, argv, "a:c:k:hp:v")) != -1) {
|
||||
switch(c) {
|
||||
case 'a':
|
||||
addr = optarg;
|
||||
break;
|
||||
case 'c':
|
||||
cert = optarg;
|
||||
break;
|
||||
case 'k':
|
||||
key = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
verb++;
|
||||
break;
|
||||
case '?':
|
||||
case 'h':
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if(argc != 0)
|
||||
usage();
|
||||
|
||||
#ifdef SIGPIPE
|
||||
(void)signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
ERR_load_crypto_strings();
|
||||
ERR_load_SSL_strings();
|
||||
OpenSSL_add_all_algorithms();
|
||||
(void)SSL_library_init();
|
||||
|
||||
do_service(addr, port, key, cert);
|
||||
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
ERR_remove_state(0);
|
||||
ERR_free_strings();
|
||||
RAND_cleanup();
|
||||
return 0;
|
||||
}
|
||||
202
external/unbound/testcode/pktview.c
vendored
Normal file
202
external/unbound/testcode/pktview.c
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* testcode/pktview.c - debug program to disassemble a DNS packet.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This program shows a dns packet wire format.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "testcode/unitmain.h"
|
||||
#include "testcode/readhex.h"
|
||||
#include "ldns/sbuffer.h"
|
||||
#include "ldns/parseutil.h"
|
||||
|
||||
/** usage information for pktview */
|
||||
static void usage(char* argv[])
|
||||
{
|
||||
printf("usage: %s\n", argv[0]);
|
||||
printf("present hex packet on stdin.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** read hex input */
|
||||
static void read_input(sldns_buffer* pkt, FILE* in)
|
||||
{
|
||||
char buf[102400];
|
||||
char* np = buf;
|
||||
while(fgets(np, (int)sizeof(buf) - (np-buf), in)) {
|
||||
if(buf[0] == ';') /* comment */
|
||||
continue;
|
||||
np = &np[strlen(np)];
|
||||
}
|
||||
hex_to_buf(pkt, buf);
|
||||
}
|
||||
|
||||
/** analyze domain name in packet, possibly compressed */
|
||||
static void analyze_dname(sldns_buffer* pkt)
|
||||
{
|
||||
size_t oldpos = sldns_buffer_position(pkt);
|
||||
size_t len;
|
||||
printf("[pos %d] dname: ", (int)oldpos);
|
||||
dname_print(stdout, pkt, sldns_buffer_current(pkt));
|
||||
len = pkt_dname_len(pkt);
|
||||
printf(" len=%d", (int)len);
|
||||
if(sldns_buffer_position(pkt)-oldpos != len)
|
||||
printf(" comprlen=%d\n",
|
||||
(int)(sldns_buffer_position(pkt)-oldpos));
|
||||
else printf("\n");
|
||||
}
|
||||
|
||||
/** analyze rdata in packet */
|
||||
static void analyze_rdata(sldns_buffer*pkt, const sldns_rr_descriptor* desc,
|
||||
uint16_t rdlen)
|
||||
{
|
||||
int rdf = 0;
|
||||
int count = (int)desc->_dname_count;
|
||||
size_t len, oldpos;
|
||||
while(rdlen > 0 && count) {
|
||||
switch(desc->_wireformat[rdf]) {
|
||||
case LDNS_RDF_TYPE_DNAME:
|
||||
oldpos = sldns_buffer_position(pkt);
|
||||
analyze_dname(pkt);
|
||||
rdlen -= sldns_buffer_position(pkt)-oldpos;
|
||||
count --;
|
||||
len = 0;
|
||||
break;
|
||||
case LDNS_RDF_TYPE_STR:
|
||||
len = sldns_buffer_current(pkt)[0] + 1;
|
||||
break;
|
||||
default:
|
||||
len = get_rdf_size(desc->_wireformat[rdf]);
|
||||
}
|
||||
if(len) {
|
||||
printf(" wf[%d]", (int)len);
|
||||
sldns_buffer_skip(pkt, (ssize_t)len);
|
||||
rdlen -= len;
|
||||
}
|
||||
rdf++;
|
||||
}
|
||||
if(rdlen) {
|
||||
size_t i;
|
||||
printf(" remain[%d]\n", (int)rdlen);
|
||||
for(i=0; i<rdlen; i++)
|
||||
printf(" %2.2X", (unsigned)sldns_buffer_current(pkt)[i]);
|
||||
printf("\n");
|
||||
}
|
||||
else printf("\n");
|
||||
sldns_buffer_skip(pkt, (ssize_t)rdlen);
|
||||
}
|
||||
|
||||
/** analyze rr in packet */
|
||||
static void analyze_rr(sldns_buffer* pkt, int q)
|
||||
{
|
||||
uint16_t type, dclass, len;
|
||||
uint32_t ttl;
|
||||
analyze_dname(pkt);
|
||||
type = sldns_buffer_read_u16(pkt);
|
||||
dclass = sldns_buffer_read_u16(pkt);
|
||||
printf("type %s(%d)", sldns_rr_descript(type)?
|
||||
sldns_rr_descript(type)->_name: "??" , (int)type);
|
||||
printf(" class %s(%d) ", sldns_lookup_by_id(sldns_rr_classes,
|
||||
(int)dclass)?sldns_lookup_by_id(sldns_rr_classes,
|
||||
(int)dclass)->name:"??", (int)dclass);
|
||||
if(q) {
|
||||
printf("\n");
|
||||
} else {
|
||||
ttl = sldns_buffer_read_u32(pkt);
|
||||
printf(" ttl %d (0x%x)", (int)ttl, (unsigned)ttl);
|
||||
len = sldns_buffer_read_u16(pkt);
|
||||
printf(" rdata len %d:\n", (int)len);
|
||||
if(sldns_rr_descript(type))
|
||||
analyze_rdata(pkt, sldns_rr_descript(type), len);
|
||||
else sldns_buffer_skip(pkt, (ssize_t)len);
|
||||
}
|
||||
}
|
||||
|
||||
/** analyse pkt */
|
||||
static void analyze(sldns_buffer* pkt)
|
||||
{
|
||||
uint16_t i, f, qd, an, ns, ar;
|
||||
int rrnum = 0;
|
||||
printf("packet length %d\n", (int)sldns_buffer_limit(pkt));
|
||||
if(sldns_buffer_limit(pkt) < 12) return;
|
||||
|
||||
i = sldns_buffer_read_u16(pkt);
|
||||
printf("id (hostorder): %d (0x%x)\n", (int)i, (unsigned)i);
|
||||
f = sldns_buffer_read_u16(pkt);
|
||||
printf("flags: 0x%x\n", (unsigned)f);
|
||||
qd = sldns_buffer_read_u16(pkt);
|
||||
printf("qdcount: %d\n", (int)qd);
|
||||
an = sldns_buffer_read_u16(pkt);
|
||||
printf("ancount: %d\n", (int)an);
|
||||
ns = sldns_buffer_read_u16(pkt);
|
||||
printf("nscount: %d\n", (int)ns);
|
||||
ar = sldns_buffer_read_u16(pkt);
|
||||
printf("arcount: %d\n", (int)ar);
|
||||
|
||||
printf(";-- query section\n");
|
||||
while(sldns_buffer_remaining(pkt) > 0) {
|
||||
if(rrnum == (int)qd)
|
||||
printf(";-- answer section\n");
|
||||
if(rrnum == (int)qd+(int)an)
|
||||
printf(";-- authority section\n");
|
||||
if(rrnum == (int)qd+(int)an+(int)ns)
|
||||
printf(";-- additional section\n");
|
||||
printf("rr %d ", rrnum);
|
||||
analyze_rr(pkt, rrnum < (int)qd);
|
||||
rrnum++;
|
||||
}
|
||||
}
|
||||
|
||||
/** main program for pktview */
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
sldns_buffer* pkt = sldns_buffer_new(65553);
|
||||
if(argc != 1) {
|
||||
usage(argv);
|
||||
}
|
||||
if(!pkt) fatal_exit("out of memory");
|
||||
|
||||
read_input(pkt, stdin);
|
||||
analyze(pkt);
|
||||
|
||||
sldns_buffer_free(pkt);
|
||||
return 0;
|
||||
}
|
||||
85
external/unbound/testcode/readhex.c
vendored
Normal file
85
external/unbound/testcode/readhex.c
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* testcode/readhex.c - read hex data.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* \file
|
||||
* Declarations useful for the unit tests.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <ctype.h>
|
||||
#include "testcode/readhex.h"
|
||||
#include "util/log.h"
|
||||
#include "ldns/sbuffer.h"
|
||||
#include "ldns/parseutil.h"
|
||||
|
||||
/** skip whitespace */
|
||||
static void
|
||||
skip_whites(const char** p)
|
||||
{
|
||||
while(1) {
|
||||
while(isspace((int)**p))
|
||||
(*p)++;
|
||||
if(**p == ';') {
|
||||
/* comment, skip until newline */
|
||||
while(**p && **p != '\n')
|
||||
(*p)++;
|
||||
if(**p == '\n')
|
||||
(*p)++;
|
||||
} else return;
|
||||
}
|
||||
}
|
||||
|
||||
/* takes a hex string and puts into buffer */
|
||||
void hex_to_buf(sldns_buffer* pkt, const char* hex)
|
||||
{
|
||||
const char* p = hex;
|
||||
int val;
|
||||
sldns_buffer_clear(pkt);
|
||||
while(*p) {
|
||||
skip_whites(&p);
|
||||
if(sldns_buffer_position(pkt) == sldns_buffer_limit(pkt))
|
||||
fatal_exit("hex_to_buf: buffer too small");
|
||||
if(!isalnum((int)*p))
|
||||
break;
|
||||
val = sldns_hexdigit_to_int(*p++) << 4;
|
||||
skip_whites(&p);
|
||||
log_assert(*p && isalnum((int)*p));
|
||||
val |= sldns_hexdigit_to_int(*p++);
|
||||
sldns_buffer_write_u8(pkt, (uint8_t)val);
|
||||
skip_whites(&p);
|
||||
}
|
||||
sldns_buffer_flip(pkt);
|
||||
}
|
||||
|
||||
52
external/unbound/testcode/readhex.h
vendored
Normal file
52
external/unbound/testcode/readhex.h
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* testcode/readhex.h - read hex data.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* \file
|
||||
* Declarations useful for the unit tests.
|
||||
*/
|
||||
|
||||
#ifndef TESTCODE_READHEX_H
|
||||
#define TESTCODE_READHEX_H
|
||||
struct sldns_buffer;
|
||||
|
||||
/**
|
||||
* Helper to convert hex string to packet buffer.
|
||||
* @param pkt: buffer to put result in.
|
||||
* @param hex: string of hex data. Spaces and ';...' comments are skipped.
|
||||
*/
|
||||
void hex_to_buf(struct sldns_buffer* pkt, const char* hex);
|
||||
|
||||
#endif /* TESTCODE_READHEX_H */
|
||||
1026
external/unbound/testcode/replay.c
vendored
Normal file
1026
external/unbound/testcode/replay.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
458
external/unbound/testcode/replay.h
vendored
Normal file
458
external/unbound/testcode/replay.h
vendored
Normal file
|
|
@ -0,0 +1,458 @@
|
|||
/*
|
||||
* testcode/replay.h - store and use a replay of events for the DNS resolver.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Store and use a replay of events for the DNS resolver.
|
||||
* Used to test known scenarios to get known outcomes.
|
||||
*
|
||||
* <pre>
|
||||
* File format for replay files.
|
||||
*
|
||||
* ; unbound.conf options.
|
||||
* ; ...
|
||||
* ; additional commandline options to pass to unbound
|
||||
* COMMANDLINE cmdline_option
|
||||
* ; autotrust key file contents, also adds auto-trust-anchor-file: "x" to cfg
|
||||
* AUTOTRUST_FILE id
|
||||
* ; contents of that file
|
||||
* AUTOTRUST_END
|
||||
* CONFIG_END
|
||||
* ; comment line.
|
||||
* SCENARIO_BEGIN name_of_scenario
|
||||
* RANGE_BEGIN start_time end_time
|
||||
* ; give ip of the virtual server, it matches any ip if not present.
|
||||
* ADDRESS ip_address
|
||||
* match_entries
|
||||
* RANGE_END
|
||||
* ; more RANGE items.
|
||||
* ; go to the next moment
|
||||
* STEP time_step event_type [ADDRESS ip_address]
|
||||
* ; event_type can be:
|
||||
* o NOTHING - nothing
|
||||
* o QUERY - followed by entry
|
||||
* o CHECK_ANSWER - followed by entry
|
||||
* o CHECK_OUT_QUERY - followed by entry (if copy-id it is also reply).
|
||||
* o REPLY - followed by entry
|
||||
* o TIMEOUT
|
||||
* o TIME_PASSES ELAPSE [seconds] - increase 'now' time counter, can be
|
||||
* a floating point number.
|
||||
* TIME_PASSES EVAL [macro] - expanded for seconds to move time.
|
||||
* o TRAFFIC - like CHECK_ANSWER, causes traffic to flow.
|
||||
* actually the traffic flows before this step is taken.
|
||||
* the step waits for traffic to stop.
|
||||
* o CHECK_AUTOTRUST [id] - followed by FILE_BEGIN [to match] FILE_END.
|
||||
* The file contents is macro expanded before match.
|
||||
* o INFRA_RTT [ip] [dp] [rtt] - update infra cache entry with rtt.
|
||||
* o ERROR
|
||||
* ; following entry starts on the next line, ENTRY_BEGIN.
|
||||
* ; more STEP items
|
||||
* SCENARIO_END
|
||||
*
|
||||
* Calculations, a macro-like system: ${$myvar + 3600}
|
||||
* STEP 10 ASSIGN myvar = 3600
|
||||
* ; ASSIGN event. '=' is syntactic sugar here. 3600 is some expression.
|
||||
* ${..} is macro expanded from its expression. Text substitution.
|
||||
* o $var replaced with its value. var is identifier [azAZ09_]*
|
||||
* o number is that number.
|
||||
* o ${variables and arithmetic }
|
||||
* o +, -, / and *. Note, evaluated left-to-right. Use ${} for brackets.
|
||||
* So again, no precedence rules, so 2+3*4 == ${2+3}*4 = 20.
|
||||
* Do 2+${3*4} to get 24.
|
||||
* o ${function params}
|
||||
* o ${time} is the current time for the simulated unbound.
|
||||
* o ${ctime value} is the text ctime(value), Fri 3 Aug 2009, ...
|
||||
* o ${timeout} is the time until next timeout in comm_timer list.
|
||||
* o ${range lower value upper} checks if lower<=value<=upper
|
||||
* returns value if check succeeds.
|
||||
*
|
||||
* ; Example file
|
||||
* SCENARIO_BEGIN Example scenario
|
||||
* RANGE_BEGIN 0 100
|
||||
* ENTRY_BEGIN
|
||||
* ; precoded answers to queries.
|
||||
* ENTRY_END
|
||||
* END_RANGE
|
||||
* STEP 0 QUERY
|
||||
* ENTRY_BEGIN
|
||||
* ; query
|
||||
* ENTRY_END
|
||||
* ; a query is sent out to the network by resolver.
|
||||
* ; precoded answer from range is returned.
|
||||
* ; algorithm will do precoded answers from RANGE immediately, except if
|
||||
* ; the next step specifically checks for that OUT_QUERY.
|
||||
* ; or if none of the precoded answers match.
|
||||
* STEP 1 CHECK_ANSWER
|
||||
* ENTRY_BEGIN
|
||||
* ; what the reply should look like
|
||||
* ENTRY_END
|
||||
* ; successful termination. (if the answer was OK).
|
||||
* ; also, all answers must have been checked with CHECK_ANSWER.
|
||||
* ; and, no more pending out_queries (that have not been checked).
|
||||
* SCENARIO_END
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
#ifndef TESTCODE_REPLAY_H
|
||||
#define TESTCODE_REPLAY_H
|
||||
#include "util/netevent.h"
|
||||
#include "testcode/testpkts.h"
|
||||
#include "util/rbtree.h"
|
||||
struct replay_answer;
|
||||
struct replay_moment;
|
||||
struct replay_range;
|
||||
struct fake_pending;
|
||||
struct fake_timer;
|
||||
struct replay_var;
|
||||
struct infra_cache;
|
||||
struct sldns_buffer;
|
||||
|
||||
/**
|
||||
* A replay scenario.
|
||||
*/
|
||||
struct replay_scenario {
|
||||
/** name of replay scenario. malloced string. */
|
||||
char* title;
|
||||
|
||||
/** The list of replay moments. Linked list. Time increases in list. */
|
||||
struct replay_moment* mom_first;
|
||||
/** The last element in list of replay moments. */
|
||||
struct replay_moment* mom_last;
|
||||
|
||||
/**
|
||||
* List of matching answers. This is to ease replay scenario
|
||||
* creation. It lists queries (to the network) and what answer
|
||||
* should be returned. The matching answers are valid for a range
|
||||
* of time steps.
|
||||
* So: timestep, parts of query, destination --> answer.
|
||||
*/
|
||||
struct replay_range* range_list;
|
||||
};
|
||||
|
||||
/**
|
||||
* A replay moment.
|
||||
* Basically, it consists of events to a fake select() call.
|
||||
* This is a recording of an event that happens.
|
||||
* And if output is presented, what is done with that.
|
||||
*/
|
||||
struct replay_moment {
|
||||
/**
|
||||
* The replay time step number. Starts at 0, time is incremented
|
||||
* every time the fake select() is run.
|
||||
*/
|
||||
int time_step;
|
||||
/** Next replay moment in list of replay moments. */
|
||||
struct replay_moment* mom_next;
|
||||
|
||||
/** what happens this moment? */
|
||||
enum replay_event_type {
|
||||
/** nothing happens, as if this event is not there. */
|
||||
repevt_nothing,
|
||||
/** incoming query */
|
||||
repevt_front_query,
|
||||
/** test fails if reply to query does not match */
|
||||
repevt_front_reply,
|
||||
/** timeout */
|
||||
repevt_timeout,
|
||||
/** time passes */
|
||||
repevt_time_passes,
|
||||
/** reply arrives from the network */
|
||||
repevt_back_reply,
|
||||
/** test fails if query to the network does not match */
|
||||
repevt_back_query,
|
||||
/** check autotrust key file */
|
||||
repevt_autotrust_check,
|
||||
/** an error happens to outbound query */
|
||||
repevt_error,
|
||||
/** assignment to a variable */
|
||||
repevt_assign,
|
||||
/** store infra rtt cache entry: addr and string (int) */
|
||||
repevt_infra_rtt,
|
||||
/** cause traffic to flow */
|
||||
repevt_traffic
|
||||
}
|
||||
/** variable with what is to happen this moment */
|
||||
evt_type;
|
||||
|
||||
/** The sent packet must match this. Incoming events, the data. */
|
||||
struct entry* match;
|
||||
|
||||
/** the amount of time that passes */
|
||||
struct timeval elapse;
|
||||
|
||||
/** address that must be matched, or packet remote host address. */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr, if 0, then any address will do */
|
||||
socklen_t addrlen;
|
||||
|
||||
/** macro name, for assign. */
|
||||
char* variable;
|
||||
/** string argument, for assign. */
|
||||
char* string;
|
||||
|
||||
/** the autotrust file id to check */
|
||||
char* autotrust_id;
|
||||
/** file contents to match, one string per line */
|
||||
struct config_strlist* file_content;
|
||||
};
|
||||
|
||||
/**
|
||||
* Range of timesteps, and canned replies to matching queries.
|
||||
*/
|
||||
struct replay_range {
|
||||
/** time range when this is valid. Including start and end step. */
|
||||
int start_step;
|
||||
/** end step of time range. */
|
||||
int end_step;
|
||||
/** address of where this range is served. */
|
||||
struct sockaddr_storage addr;
|
||||
/** length of addr, if 0, then any address will do */
|
||||
socklen_t addrlen;
|
||||
|
||||
/** Matching list */
|
||||
struct entry* match;
|
||||
|
||||
/** next in list of time ranges. */
|
||||
struct replay_range* next_range;
|
||||
};
|
||||
|
||||
/**
|
||||
* Replay storage of runtime information.
|
||||
*/
|
||||
struct replay_runtime {
|
||||
/**
|
||||
* The scenario
|
||||
*/
|
||||
struct replay_scenario* scenario;
|
||||
/**
|
||||
* Current moment.
|
||||
*/
|
||||
struct replay_moment* now;
|
||||
|
||||
/**
|
||||
* List of pending queries in order they were sent out. First
|
||||
* one has been sent out most recently. Last one in list is oldest.
|
||||
*/
|
||||
struct fake_pending* pending_list;
|
||||
|
||||
/**
|
||||
* List of answers to queries from clients. These need to be checked.
|
||||
*/
|
||||
struct replay_answer* answer_list;
|
||||
|
||||
/** last element in answer list. */
|
||||
struct replay_answer* answer_last;
|
||||
|
||||
/** list of fake timer callbacks that are pending */
|
||||
struct fake_timer* timer_list;
|
||||
|
||||
/** callback to call for incoming queries */
|
||||
comm_point_callback_t* callback_query;
|
||||
/** user argument for incoming query callback */
|
||||
void *cb_arg;
|
||||
|
||||
/** ref the infra cache (was passed to outside_network_create) */
|
||||
struct infra_cache* infra;
|
||||
|
||||
/** the current time in seconds */
|
||||
time_t now_secs;
|
||||
/** the current time in microseconds */
|
||||
struct timeval now_tv;
|
||||
|
||||
/** signal handler callback */
|
||||
void (*sig_cb)(int, void*);
|
||||
/** signal handler user arg */
|
||||
void *sig_cb_arg;
|
||||
/** time to exit cleanly */
|
||||
int exit_cleanly;
|
||||
|
||||
/** size of buffers */
|
||||
size_t bufsize;
|
||||
|
||||
/**
|
||||
* Tree of macro values. Of type replay_var
|
||||
*/
|
||||
rbtree_t* vars;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pending queries to network, fake replay version.
|
||||
*/
|
||||
struct fake_pending {
|
||||
/** what is important only that we remember the query, copied here. */
|
||||
struct sldns_buffer* buffer;
|
||||
/** and to what address this is sent to. */
|
||||
struct sockaddr_storage addr;
|
||||
/** len of addr */
|
||||
socklen_t addrlen;
|
||||
/** zone name, uncompressed wire format (as used when sent) */
|
||||
uint8_t* zone;
|
||||
/** length of zone name */
|
||||
size_t zonelen;
|
||||
/** qtype */
|
||||
int qtype;
|
||||
/** The callback function to call when answer arrives (or timeout) */
|
||||
comm_point_callback_t* callback;
|
||||
/** callback user argument */
|
||||
void* cb_arg;
|
||||
/** original timeout in seconds from 'then' */
|
||||
int timeout;
|
||||
|
||||
/** next in pending list */
|
||||
struct fake_pending* next;
|
||||
/** the buffer parsed into a sldns_pkt */
|
||||
uint8_t* pkt;
|
||||
size_t pkt_len;
|
||||
/** by what transport was the query sent out */
|
||||
enum transport_type transport;
|
||||
/** if this is a serviced query */
|
||||
int serviced;
|
||||
/** the runtime structure this is part of */
|
||||
struct replay_runtime* runtime;
|
||||
};
|
||||
|
||||
/**
|
||||
* An answer that is pending to happen.
|
||||
*/
|
||||
struct replay_answer {
|
||||
/** Next in list */
|
||||
struct replay_answer* next;
|
||||
/** reply information */
|
||||
struct comm_reply repinfo;
|
||||
/** the answer preparsed as ldns pkt */
|
||||
uint8_t* pkt;
|
||||
size_t pkt_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* Timers with callbacks, fake replay version.
|
||||
*/
|
||||
struct fake_timer {
|
||||
/** next in list */
|
||||
struct fake_timer* next;
|
||||
/** the runtime structure this is part of */
|
||||
struct replay_runtime* runtime;
|
||||
/** the callback to call */
|
||||
void (*cb)(void*);
|
||||
/** the callback user argument */
|
||||
void* cb_arg;
|
||||
/** if timer is enabled */
|
||||
int enabled;
|
||||
/** when the timer expires */
|
||||
struct timeval tv;
|
||||
};
|
||||
|
||||
/**
|
||||
* Replay macro variable. And its value.
|
||||
*/
|
||||
struct replay_var {
|
||||
/** rbtree node. Key is this structure. Sorted by name. */
|
||||
rbnode_t node;
|
||||
/** the variable name */
|
||||
char* name;
|
||||
/** the variable value */
|
||||
char* value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Read a replay scenario from the file.
|
||||
* @param in: file to read from.
|
||||
* @param name: name to print in errors.
|
||||
* @param lineno: incremented for every line read.
|
||||
* @return: Scenario. NULL if no scenario read.
|
||||
*/
|
||||
struct replay_scenario* replay_scenario_read(FILE* in, const char* name,
|
||||
int* lineno);
|
||||
|
||||
/**
|
||||
* Delete scenario.
|
||||
* @param scen: to delete.
|
||||
*/
|
||||
void replay_scenario_delete(struct replay_scenario* scen);
|
||||
|
||||
/** compare two replay_vars */
|
||||
int replay_var_compare(const void* a, const void* b);
|
||||
|
||||
/** get oldest enabled fake timer */
|
||||
struct fake_timer* replay_get_oldest_timer(struct replay_runtime* runtime);
|
||||
|
||||
/**
|
||||
* Create variable storage
|
||||
* @return new or NULL on failure.
|
||||
*/
|
||||
rbtree_t* macro_store_create(void);
|
||||
|
||||
/**
|
||||
* Delete variable storage
|
||||
* @param store: the macro storage to free up.
|
||||
*/
|
||||
void macro_store_delete(rbtree_t* store);
|
||||
|
||||
/**
|
||||
* Apply macro substitution to string.
|
||||
* @param store: variable store.
|
||||
* @param runtime: the runtime to look up values as needed.
|
||||
* @param text: string to work on.
|
||||
* @return newly malloced string with result.
|
||||
*/
|
||||
char* macro_process(rbtree_t* store, struct replay_runtime* runtime,
|
||||
char* text);
|
||||
|
||||
/**
|
||||
* Look up a macro value. Like calling ${$name}.
|
||||
* @param store: variable store
|
||||
* @param name: macro name
|
||||
* @return newly malloced string with result or strdup("") if not found.
|
||||
* or NULL on malloc failure.
|
||||
*/
|
||||
char* macro_lookup(rbtree_t* store, char* name);
|
||||
|
||||
/**
|
||||
* Set macro value.
|
||||
* @param store: variable store
|
||||
* @param name: macro name
|
||||
* @param value: text to set it to. Not expanded.
|
||||
* @return false on failure.
|
||||
*/
|
||||
int macro_assign(rbtree_t* store, char* name, char* value);
|
||||
|
||||
/** Print macro variables stored as debug info */
|
||||
void macro_print_debug(rbtree_t* store);
|
||||
|
||||
/** testbounds self test */
|
||||
void testbound_selftest(void);
|
||||
|
||||
#endif /* TESTCODE_REPLAY_H */
|
||||
78
external/unbound/testcode/run_vm.sh
vendored
Normal file
78
external/unbound/testcode/run_vm.sh
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#!/usr/local/bin/bash
|
||||
# run tpkg tests from within a VM. Looks for loopback addr.
|
||||
# if run not from within a VM, runs the tests as usual.
|
||||
# with one argument: run that tpkg, otherwise, run all tpkgs.
|
||||
|
||||
get_lo0_ip4() {
|
||||
if test -x /sbin/ifconfig
|
||||
then
|
||||
LO0_IP4=`/sbin/ifconfig lo0 | grep '[^0-9]127\.' | sed -e 's/^[^1]*\(127\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)[^0-9]*.*$/\1/g'`
|
||||
if ( echo $LO0_IP4 | grep '^127\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' > /dev/null )
|
||||
then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
LO0_IP4=127.0.0.1
|
||||
}
|
||||
get_lo0_ip4
|
||||
export LO0_IP4
|
||||
if test "x$LO0_IP4" = "x127.0.0.1"
|
||||
then
|
||||
ALT_LOOPBACK=false
|
||||
else
|
||||
ALT_LOOPBACK=true
|
||||
fi
|
||||
cd testdata
|
||||
TPKG=../testcode/mini_tpkg.sh
|
||||
#RUNLIST=`(ls -1 *.tpkg|grep -v '^0[016]')`
|
||||
RUNLIST=`(ls -1 *.tpkg)`
|
||||
if test "$#" = "1"; then RUNLIST="$1"; fi
|
||||
|
||||
# fix up tpkg that was edited on keyboard interrupt.
|
||||
cleanup() {
|
||||
echo cleanup
|
||||
if test -f "$t.bak"; then mv "$t.bak" "$t"; fi
|
||||
exit 0
|
||||
}
|
||||
trap cleanup SIGINT
|
||||
|
||||
for t in $RUNLIST
|
||||
do
|
||||
if ! $ALT_LOOPBACK
|
||||
then
|
||||
$TPKG exe $t
|
||||
continue
|
||||
fi
|
||||
# We have alternative 127.0.0.1 number
|
||||
if ( echo $t | grep '6\.tpkg$' ) # skip IPv6 tests
|
||||
then
|
||||
continue
|
||||
elif test "$t" = "edns_cache.tpkg" # This one is IPv6 too!
|
||||
then
|
||||
continue
|
||||
fi
|
||||
cp -p "$t" "$t.bak"
|
||||
tar xzf $t
|
||||
find "${t%.tpkg}.dir" -type f \
|
||||
-exec grep -q -e '127\.0\.0\.1' -e '@localhost' {} \; -print | {
|
||||
while read f
|
||||
do
|
||||
sed "s/127\.0\.0\.1/${LO0_IP4}/g" "$f" > "$f._"
|
||||
mv "$f._" "$f"
|
||||
sed "s/@localhost/@${LO0_IP4}/g" "$f" > "$f._"
|
||||
mv "$f._" "$f"
|
||||
done
|
||||
}
|
||||
find "${t%.tpkg}.dir" -type d -name "127.0.0.1" -print | {
|
||||
while read d
|
||||
do
|
||||
mv -v "$d" "${d%127.0.0.1}${LO0_IP4}"
|
||||
done
|
||||
}
|
||||
tar czf $t "${t%.tpkg}.dir"
|
||||
rm -fr "${t%.tpkg}.dir"
|
||||
$TPKG exe $t
|
||||
mv "$t.bak" "$t"
|
||||
done
|
||||
# get out of testdata/
|
||||
cd ..
|
||||
284
external/unbound/testcode/signit.c
vendored
Normal file
284
external/unbound/testcode/signit.c
vendored
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* testcode/signit.c - debug tool to sign rrsets with given keys.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This program signs rrsets with the given keys. It can be used to
|
||||
* construct input to test the validator with.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <ldns/ldns.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define DNSKEY_BIT_ZSK 0x0100
|
||||
|
||||
/**
|
||||
* Key settings
|
||||
*/
|
||||
struct keysets {
|
||||
/** signature inception */
|
||||
uint32_t incep;
|
||||
/** signature expiration */
|
||||
uint32_t expi;
|
||||
/** owner name */
|
||||
char* owner;
|
||||
/** keytag */
|
||||
uint16_t keytag;
|
||||
/** DNSKEY flags */
|
||||
uint16_t flags;
|
||||
};
|
||||
|
||||
/** print usage and exit */
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
printf("usage: signit expi ince keytag owner keyfile\n");
|
||||
printf("present rrset data on stdin.\n");
|
||||
printf("signed data is printed to stdout.\n");
|
||||
printf("\n");
|
||||
printf("Or use: signit NSEC3PARAM hash flags iter salt\n");
|
||||
printf("present names on stdin, hashed names are printed to stdout.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static time_t
|
||||
convert_timeval(const char* str)
|
||||
{
|
||||
time_t t;
|
||||
struct tm tm;
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
if(strlen(str) < 14)
|
||||
return 0;
|
||||
if(sscanf(str, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon,
|
||||
&tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6)
|
||||
return 0;
|
||||
tm.tm_year -= 1900;
|
||||
tm.tm_mon--;
|
||||
/* Check values */
|
||||
if (tm.tm_year < 70) return 0;
|
||||
if (tm.tm_mon < 0 || tm.tm_mon > 11) return 0;
|
||||
if (tm.tm_mday < 1 || tm.tm_mday > 31) return 0;
|
||||
if (tm.tm_hour < 0 || tm.tm_hour > 23) return 0;
|
||||
if (tm.tm_min < 0 || tm.tm_min > 59) return 0;
|
||||
if (tm.tm_sec < 0 || tm.tm_sec > 59) return 0;
|
||||
/* call ldns conversion function */
|
||||
t = ldns_mktime_from_utc(&tm);
|
||||
return t;
|
||||
}
|
||||
|
||||
static void fatal_exit(const char* format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
printf("fatal exit: ");
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** read expi ince keytag owner from cmdline */
|
||||
static void
|
||||
parse_cmdline(char *argv[], struct keysets* s)
|
||||
{
|
||||
s->expi = convert_timeval(argv[1]);
|
||||
s->incep = convert_timeval(argv[2]);
|
||||
s->keytag = (uint16_t)atoi(argv[3]);
|
||||
s->owner = argv[4];
|
||||
s->flags = DNSKEY_BIT_ZSK; /* to enforce signing */
|
||||
}
|
||||
|
||||
/** read all key files, exit on error */
|
||||
static ldns_key_list*
|
||||
read_keys(int num, char* names[], struct keysets* set)
|
||||
{
|
||||
int i;
|
||||
ldns_key_list* keys = ldns_key_list_new();
|
||||
ldns_key* k;
|
||||
ldns_rdf* rdf;
|
||||
ldns_status s;
|
||||
int b;
|
||||
FILE* in;
|
||||
|
||||
if(!keys) fatal_exit("alloc failure");
|
||||
for(i=0; i<num; i++) {
|
||||
printf("read keyfile %s\n", names[i]);
|
||||
in = fopen(names[i], "r");
|
||||
if(!in) fatal_exit("could not open %s: %s", names[i],
|
||||
strerror(errno));
|
||||
s = ldns_key_new_frm_fp(&k, in);
|
||||
fclose(in);
|
||||
if(s != LDNS_STATUS_OK)
|
||||
fatal_exit("bad keyfile %s: %s", names[i],
|
||||
ldns_get_errorstr_by_id(s));
|
||||
ldns_key_set_expiration(k, set->expi);
|
||||
ldns_key_set_inception(k, set->incep);
|
||||
s = ldns_str2rdf_dname(&rdf, set->owner);
|
||||
if(s != LDNS_STATUS_OK)
|
||||
fatal_exit("bad owner name %s: %s", set->owner,
|
||||
ldns_get_errorstr_by_id(s));
|
||||
ldns_key_set_pubkey_owner(k, rdf);
|
||||
ldns_key_set_flags(k, set->flags);
|
||||
ldns_key_set_keytag(k, set->keytag);
|
||||
b = ldns_key_list_push_key(keys, k);
|
||||
assert(b);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
/** read list of rrs from the file */
|
||||
static ldns_rr_list*
|
||||
read_rrs(FILE* in)
|
||||
{
|
||||
uint32_t my_ttl = 3600;
|
||||
ldns_rdf *my_origin = NULL;
|
||||
ldns_rdf *my_prev = NULL;
|
||||
ldns_status s;
|
||||
int line_nr = 1;
|
||||
int b;
|
||||
|
||||
ldns_rr_list* list;
|
||||
ldns_rr *rr;
|
||||
|
||||
list = ldns_rr_list_new();
|
||||
if(!list) fatal_exit("alloc error");
|
||||
|
||||
while(!feof(in)) {
|
||||
s = ldns_rr_new_frm_fp_l(&rr, in, &my_ttl, &my_origin,
|
||||
&my_prev, &line_nr);
|
||||
if(s == LDNS_STATUS_SYNTAX_TTL ||
|
||||
s == LDNS_STATUS_SYNTAX_ORIGIN ||
|
||||
s == LDNS_STATUS_SYNTAX_EMPTY)
|
||||
continue;
|
||||
else if(s != LDNS_STATUS_OK)
|
||||
fatal_exit("parse error in line %d: %s", line_nr,
|
||||
ldns_get_errorstr_by_id(s));
|
||||
b = ldns_rr_list_push_rr(list, rr);
|
||||
assert(b);
|
||||
}
|
||||
printf("read %d lines\n", line_nr);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/** sign the rrs with the keys */
|
||||
static void
|
||||
signit(ldns_rr_list* rrs, ldns_key_list* keys)
|
||||
{
|
||||
ldns_rr_list* rrset;
|
||||
ldns_rr_list* sigs;
|
||||
|
||||
while(ldns_rr_list_rr_count(rrs) > 0) {
|
||||
rrset = ldns_rr_list_pop_rrset(rrs);
|
||||
if(!rrset) fatal_exit("copy alloc failure");
|
||||
sigs = ldns_sign_public(rrset, keys);
|
||||
if(!sigs) fatal_exit("failed to sign");
|
||||
ldns_rr_list_print(stdout, rrset);
|
||||
ldns_rr_list_print(stdout, sigs);
|
||||
printf("\n");
|
||||
ldns_rr_list_free(rrset);
|
||||
ldns_rr_list_free(sigs);
|
||||
}
|
||||
}
|
||||
|
||||
/** process keys and signit */
|
||||
static void
|
||||
process_keys(int argc, char* argv[])
|
||||
{
|
||||
ldns_rr_list* rrs;
|
||||
ldns_key_list* keys;
|
||||
struct keysets settings;
|
||||
assert(argc == 6);
|
||||
|
||||
parse_cmdline(argv, &settings);
|
||||
keys = read_keys(1, argv+5, &settings);
|
||||
rrs = read_rrs(stdin);
|
||||
signit(rrs, keys);
|
||||
|
||||
ldns_rr_list_deep_free(rrs);
|
||||
ldns_key_list_free(keys);
|
||||
}
|
||||
|
||||
/** process nsec3 params and perform hashing */
|
||||
static void
|
||||
process_nsec3(int argc, char* argv[])
|
||||
{
|
||||
char line[10240];
|
||||
ldns_rdf* salt;
|
||||
ldns_rdf* in, *out;
|
||||
ldns_status status;
|
||||
status = ldns_str2rdf_nsec3_salt(&salt, argv[5]);
|
||||
if(status != LDNS_STATUS_OK)
|
||||
fatal_exit("Could not parse salt %s: %s", argv[5],
|
||||
ldns_get_errorstr_by_id(status));
|
||||
assert(argc == 6);
|
||||
while(fgets(line, (int)sizeof(line), stdin)) {
|
||||
if(strlen(line) > 0)
|
||||
line[strlen(line)-1] = 0; /* remove trailing newline */
|
||||
if(line[0]==0)
|
||||
continue;
|
||||
status = ldns_str2rdf_dname(&in, line);
|
||||
if(status != LDNS_STATUS_OK)
|
||||
fatal_exit("Could not parse name %s: %s", line,
|
||||
ldns_get_errorstr_by_id(status));
|
||||
ldns_rdf_print(stdout, in);
|
||||
printf(" -> ");
|
||||
/* arg 3 is flags, unused */
|
||||
out = ldns_nsec3_hash_name(in, (uint8_t)atoi(argv[2]),
|
||||
(uint16_t)atoi(argv[4]),
|
||||
ldns_rdf_data(salt)[0], ldns_rdf_data(salt)+1);
|
||||
if(!out)
|
||||
fatal_exit("Could not hash %s", line);
|
||||
ldns_rdf_print(stdout, out);
|
||||
printf("\n");
|
||||
ldns_rdf_deep_free(in);
|
||||
ldns_rdf_deep_free(out);
|
||||
}
|
||||
ldns_rdf_deep_free(salt);
|
||||
}
|
||||
|
||||
/** main program */
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if(argc != 6) {
|
||||
usage();
|
||||
}
|
||||
if(strcmp(argv[1], "NSEC3PARAM") == 0) {
|
||||
process_nsec3(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
process_keys(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
66
external/unbound/testcode/streamtcp.1
vendored
Normal file
66
external/unbound/testcode/streamtcp.1
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
.TH "unbound\-streamtcp" "1" "Mar 21, 2013" "NLnet Labs" "unbound"
|
||||
.\"
|
||||
.\" unbound-streamtcp.1 -- unbound DNS lookup utility
|
||||
.\"
|
||||
.SH "NAME"
|
||||
.LP
|
||||
.B unbound\-streamtcp
|
||||
\- unbound DNS lookup utility
|
||||
.SH "SYNOPSIS"
|
||||
.LP
|
||||
.B unbound\-streamtcp
|
||||
.RB [ \-unsh ]
|
||||
.RB [ \-f
|
||||
.IR ipaddr[@port] ]
|
||||
.I name
|
||||
.I type
|
||||
.I class
|
||||
.SH "DESCRIPTION"
|
||||
.LP
|
||||
.B unbound\-streamtcp
|
||||
sends a DNS Query of the given \fBtype\fR and \fBclass\fR for the given \fBname\fR
|
||||
to the DNS server over TCP and displays the response.
|
||||
.P
|
||||
If the server to query is not given using the \fB\-f\fR option then localhost
|
||||
(127.0.0.1) is used. More queries can be given on one commandline, they
|
||||
are resolved in sequence.
|
||||
.P
|
||||
The available options are:
|
||||
.TP
|
||||
.I name
|
||||
This name is resolved (looked up in the DNS).
|
||||
.TP
|
||||
.I type
|
||||
Specify the type of data to lookup.
|
||||
.TP
|
||||
.I class
|
||||
Specify the class to lookup for.
|
||||
.TP
|
||||
.B \-u
|
||||
Use UDP instead of TCP. No retries are attempted.
|
||||
.TP
|
||||
.B \-n
|
||||
Do not wait for the answer.
|
||||
.TP
|
||||
.B \-s
|
||||
Use SSL.
|
||||
.TP
|
||||
.B \-h
|
||||
Print program usage.
|
||||
.TP
|
||||
.B \-f \fIipaddr[@port]
|
||||
Specify the server to send the queries to. If not specified localhost (127.0.0.1) is used.
|
||||
.SH "EXAMPLES"
|
||||
.LP
|
||||
Some examples of use.
|
||||
.P
|
||||
$ unbound\-streamtcp www.example.com A IN
|
||||
.P
|
||||
$ unbound\-streamtcp \-f 192.168.1.1 www.example.com SOA IN
|
||||
.P
|
||||
$ unbound\-streamtcp \-f 192.168.1.1@1234 153.1.168.192.in\-addr.arpa. PTR IN
|
||||
.SH "EXIT CODE"
|
||||
The unbound\-streamtcp program exits with status code 1 on error,
|
||||
0 on no error.
|
||||
.SH "AUTHOR"
|
||||
This manual page was written by Tomas Hozza <thozza@redhat.com>.
|
||||
418
external/unbound/testcode/streamtcp.c
vendored
Normal file
418
external/unbound/testcode/streamtcp.c
vendored
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
* testcode/streamtcp.c - debug program perform multiple DNS queries on tcp.
|
||||
*
|
||||
* Copyright (c) 2008, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This program performs multiple DNS queries on a TCP stream.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
#include <signal.h>
|
||||
#include "util/locks.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "ldns/sbuffer.h"
|
||||
#include "ldns/str2wire.h"
|
||||
#include "ldns/wire2str.h"
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#ifndef PF_INET6
|
||||
/** define in case streamtcp is compiled on legacy systems */
|
||||
#define PF_INET6 10
|
||||
#endif
|
||||
|
||||
/** usage information for streamtcp */
|
||||
static void usage(char* argv[])
|
||||
{
|
||||
printf("usage: %s [options] name type class ...\n", argv[0]);
|
||||
printf(" sends the name-type-class queries over TCP.\n");
|
||||
printf("-f server what ipaddr@portnr to send the queries to\n");
|
||||
printf("-u use UDP. No retries are attempted.\n");
|
||||
printf("-n do not wait for an answer.\n");
|
||||
printf("-s use ssl\n");
|
||||
printf("-h this help text\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** open TCP socket to svr */
|
||||
static int
|
||||
open_svr(const char* svr, int udp)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
int fd = -1;
|
||||
/* svr can be ip@port */
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
if(!extstrtoaddr(svr, &addr, &addrlen)) {
|
||||
printf("fatal: bad server specs '%s'\n", svr);
|
||||
exit(1);
|
||||
}
|
||||
fd = socket(addr_is_ip6(&addr, addrlen)?PF_INET6:PF_INET,
|
||||
udp?SOCK_DGRAM:SOCK_STREAM, 0);
|
||||
if(fd == -1) {
|
||||
#ifndef USE_WINSOCK
|
||||
perror("socket() error");
|
||||
#else
|
||||
printf("socket: %s\n", wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
if(connect(fd, (struct sockaddr*)&addr, addrlen) < 0) {
|
||||
#ifndef USE_WINSOCK
|
||||
perror("connect() error");
|
||||
#else
|
||||
printf("connect: %s\n", wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/** write a query over the TCP fd */
|
||||
static void
|
||||
write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id,
|
||||
const char* strname, const char* strtype, const char* strclass)
|
||||
{
|
||||
struct query_info qinfo;
|
||||
uint16_t len;
|
||||
/* qname */
|
||||
qinfo.qname = sldns_str2wire_dname(strname, &qinfo.qname_len);
|
||||
if(!qinfo.qname) {
|
||||
printf("cannot parse query name: '%s'\n", strname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* qtype and qclass */
|
||||
qinfo.qtype = sldns_get_rr_type_by_name(strtype);
|
||||
qinfo.qclass = sldns_get_rr_class_by_name(strclass);
|
||||
|
||||
/* make query */
|
||||
qinfo_query_encode(buf, &qinfo);
|
||||
sldns_buffer_write_u16_at(buf, 0, id);
|
||||
sldns_buffer_write_u16_at(buf, 2, BIT_RD);
|
||||
|
||||
if(1) {
|
||||
/* add EDNS DO */
|
||||
struct edns_data edns;
|
||||
memset(&edns, 0, sizeof(edns));
|
||||
edns.edns_present = 1;
|
||||
edns.bits = EDNS_DO;
|
||||
edns.udp_size = 4096;
|
||||
attach_edns_record(buf, &edns);
|
||||
}
|
||||
|
||||
/* send it */
|
||||
if(!udp) {
|
||||
len = (uint16_t)sldns_buffer_limit(buf);
|
||||
len = htons(len);
|
||||
if(ssl) {
|
||||
if(SSL_write(ssl, (void*)&len, (int)sizeof(len)) <= 0) {
|
||||
log_crypto_err("cannot SSL_write");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if(send(fd, (void*)&len, sizeof(len), 0) <
|
||||
(ssize_t)sizeof(len)){
|
||||
#ifndef USE_WINSOCK
|
||||
perror("send() len failed");
|
||||
#else
|
||||
printf("send len: %s\n",
|
||||
wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(ssl) {
|
||||
if(SSL_write(ssl, (void*)sldns_buffer_begin(buf),
|
||||
(int)sldns_buffer_limit(buf)) <= 0) {
|
||||
log_crypto_err("cannot SSL_write");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if(send(fd, (void*)sldns_buffer_begin(buf),
|
||||
sldns_buffer_limit(buf), 0) <
|
||||
(ssize_t)sldns_buffer_limit(buf)) {
|
||||
#ifndef USE_WINSOCK
|
||||
perror("send() data failed");
|
||||
#else
|
||||
printf("send data: %s\n", wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
free(qinfo.qname);
|
||||
}
|
||||
|
||||
/** receive DNS datagram over TCP and print it */
|
||||
static void
|
||||
recv_one(int fd, int udp, SSL* ssl, sldns_buffer* buf)
|
||||
{
|
||||
char* pktstr;
|
||||
uint16_t len;
|
||||
if(!udp) {
|
||||
if(ssl) {
|
||||
if(SSL_read(ssl, (void*)&len, (int)sizeof(len)) <= 0) {
|
||||
log_crypto_err("could not SSL_read");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if(recv(fd, (void*)&len, sizeof(len), 0) <
|
||||
(ssize_t)sizeof(len)) {
|
||||
#ifndef USE_WINSOCK
|
||||
perror("read() len failed");
|
||||
#else
|
||||
printf("read len: %s\n",
|
||||
wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
len = ntohs(len);
|
||||
sldns_buffer_clear(buf);
|
||||
sldns_buffer_set_limit(buf, len);
|
||||
if(ssl) {
|
||||
int r = SSL_read(ssl, (void*)sldns_buffer_begin(buf),
|
||||
(int)len);
|
||||
if(r <= 0) {
|
||||
log_crypto_err("could not SSL_read");
|
||||
exit(1);
|
||||
}
|
||||
if(r != (int)len)
|
||||
fatal_exit("ssl_read %d of %d", r, len);
|
||||
} else {
|
||||
if(recv(fd, (void*)sldns_buffer_begin(buf), len, 0) <
|
||||
(ssize_t)len) {
|
||||
#ifndef USE_WINSOCK
|
||||
perror("read() data failed");
|
||||
#else
|
||||
printf("read data: %s\n",
|
||||
wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ssize_t l;
|
||||
sldns_buffer_clear(buf);
|
||||
if((l=recv(fd, (void*)sldns_buffer_begin(buf),
|
||||
sldns_buffer_capacity(buf), 0)) < 0) {
|
||||
#ifndef USE_WINSOCK
|
||||
perror("read() data failed");
|
||||
#else
|
||||
printf("read data: %s\n",
|
||||
wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
sldns_buffer_set_limit(buf, (size_t)l);
|
||||
len = (size_t)l;
|
||||
}
|
||||
printf("\nnext received packet\n");
|
||||
log_buf(0, "data", buf);
|
||||
|
||||
pktstr = sldns_wire2str_pkt(sldns_buffer_begin(buf), len);
|
||||
printf("%s", pktstr);
|
||||
free(pktstr);
|
||||
}
|
||||
|
||||
static int get_random(void)
|
||||
{
|
||||
int r;
|
||||
if (RAND_bytes((unsigned char*)&r, (int)sizeof(r)) == 1) {
|
||||
return r;
|
||||
}
|
||||
return (int)random();
|
||||
}
|
||||
|
||||
/** send the TCP queries and print answers */
|
||||
static void
|
||||
send_em(const char* svr, int udp, int usessl, int noanswer, int num, char** qs)
|
||||
{
|
||||
sldns_buffer* buf = sldns_buffer_new(65553);
|
||||
int fd = open_svr(svr, udp);
|
||||
int i;
|
||||
SSL_CTX* ctx = NULL;
|
||||
SSL* ssl = NULL;
|
||||
if(!buf) fatal_exit("out of memory");
|
||||
if(usessl) {
|
||||
ctx = connect_sslctx_create(NULL, NULL, NULL);
|
||||
if(!ctx) fatal_exit("cannot create ssl ctx");
|
||||
ssl = outgoing_ssl_fd(ctx, fd);
|
||||
if(!ssl) fatal_exit("cannot create ssl");
|
||||
while(1) {
|
||||
int r;
|
||||
ERR_clear_error();
|
||||
if( (r=SSL_do_handshake(ssl)) == 1)
|
||||
break;
|
||||
r = SSL_get_error(ssl, r);
|
||||
if(r != SSL_ERROR_WANT_READ &&
|
||||
r != SSL_ERROR_WANT_WRITE) {
|
||||
log_crypto_err("could not ssl_handshake");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if(1) {
|
||||
X509* x = SSL_get_peer_certificate(ssl);
|
||||
if(!x) printf("SSL: no peer certificate\n");
|
||||
else {
|
||||
X509_print_fp(stdout, x);
|
||||
X509_free(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i=0; i<num; i+=3) {
|
||||
printf("\nNext query is %s %s %s\n", qs[i], qs[i+1], qs[i+2]);
|
||||
write_q(fd, udp, ssl, buf, (uint16_t)get_random(), qs[i],
|
||||
qs[i+1], qs[i+2]);
|
||||
/* print at least one result */
|
||||
if(!noanswer)
|
||||
recv_one(fd, udp, ssl, buf);
|
||||
}
|
||||
|
||||
if(usessl) {
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
SSL_CTX_free(ctx);
|
||||
}
|
||||
#ifndef USE_WINSOCK
|
||||
close(fd);
|
||||
#else
|
||||
closesocket(fd);
|
||||
#endif
|
||||
sldns_buffer_free(buf);
|
||||
printf("orderly exit\n");
|
||||
}
|
||||
|
||||
#ifdef SIGPIPE
|
||||
/** SIGPIPE handler */
|
||||
static RETSIGTYPE sigh(int sig)
|
||||
{
|
||||
if(sig == SIGPIPE) {
|
||||
printf("got SIGPIPE, remote connection gone\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("Got unhandled signal %d\n", sig);
|
||||
exit(1);
|
||||
}
|
||||
#endif /* SIGPIPE */
|
||||
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern int optind;
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern char* optarg;
|
||||
|
||||
/** main program for streamtcp */
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int c;
|
||||
const char* svr = "127.0.0.1";
|
||||
int udp = 0;
|
||||
int noanswer = 0;
|
||||
int usessl = 0;
|
||||
|
||||
#ifdef USE_WINSOCK
|
||||
WSADATA wsa_data;
|
||||
if(WSAStartup(MAKEWORD(2,2), &wsa_data) != 0) {
|
||||
printf("WSAStartup failed\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* lock debug start (if any) */
|
||||
log_init(0, 0, 0);
|
||||
checklock_start();
|
||||
|
||||
#ifdef SIGPIPE
|
||||
if(signal(SIGPIPE, &sigh) == SIG_ERR) {
|
||||
perror("could not install signal handler");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* command line options */
|
||||
if(argc == 1) {
|
||||
usage(argv);
|
||||
}
|
||||
while( (c=getopt(argc, argv, "f:hnsu")) != -1) {
|
||||
switch(c) {
|
||||
case 'f':
|
||||
svr = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
noanswer = 1;
|
||||
break;
|
||||
case 'u':
|
||||
udp = 1;
|
||||
break;
|
||||
case 's':
|
||||
usessl = 1;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
default:
|
||||
usage(argv);
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if(argc % 3 != 0) {
|
||||
printf("queries must be multiples of name,type,class\n");
|
||||
return 1;
|
||||
}
|
||||
if(usessl) {
|
||||
ERR_load_SSL_strings();
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_library_init();
|
||||
}
|
||||
send_em(svr, udp, usessl, noanswer, argc, argv);
|
||||
checklock_stop();
|
||||
#ifdef USE_WINSOCK
|
||||
WSACleanup();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
133
external/unbound/testcode/testbed.sh
vendored
Executable file
133
external/unbound/testcode/testbed.sh
vendored
Executable file
|
|
@ -0,0 +1,133 @@
|
|||
#!/usr/bin/env bash
|
||||
# Testbed for NSD.
|
||||
# By Wouter Wijngaards, NLnet Labs, 2006.
|
||||
# BSD License.
|
||||
|
||||
# this version prefers gmake if available.
|
||||
# adds variable LDNS for the LDNS path to use.
|
||||
|
||||
# global settings
|
||||
CONFIGURE_FLAGS=""
|
||||
REPORT_FILE=testdata/testbed.report
|
||||
LOG_FILE=testdata/testbed.log
|
||||
HOST_FILE=testdata/host_file.$USER
|
||||
|
||||
if test ! -f $HOST_FILE; then
|
||||
echo "No such file: $HOST_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function echossh() # like ssh but echos.
|
||||
{
|
||||
echo "> ssh $*"
|
||||
ssh $*
|
||||
}
|
||||
|
||||
# Compile and run NSD on platforms
|
||||
function dotest()
|
||||
# parameters: <host> <dir>
|
||||
# host is name of ssh host
|
||||
# dir is directory of nsd trunk on host
|
||||
{
|
||||
echo "$1 begin on "`date` | tee -a $REPORT_FILE
|
||||
|
||||
DISABLE=""
|
||||
if test $IP6 = no; then
|
||||
DISABLE="--disable-ipv6"
|
||||
fi
|
||||
if test x$LDNS != x; then
|
||||
DISABLE="--with-ldns=$LDNS $DISABLE"
|
||||
fi
|
||||
if test x$LIBEVENT != x; then
|
||||
DISABLE="--with-libevent=$LIBEVENT $DISABLE"
|
||||
fi
|
||||
|
||||
cat >makeconf.mak.$$ << EOF
|
||||
#configure: configure.ac
|
||||
# $AC_CMD
|
||||
# touch configure
|
||||
Makefile: Makefile.in #configure
|
||||
./configure $CONFIGURE_FLAGS $DISABLE
|
||||
touch Makefile
|
||||
EOF
|
||||
scp makeconf.mak.$$ $1:$2
|
||||
# determine make to use
|
||||
tempx=`ssh $1 "cd $2; which gmake"`
|
||||
MAKE_CMD=`ssh $1 "cd $2; if test -f '$tempx'; then echo $tempx; else echo $MAKE_CMD; fi"`
|
||||
|
||||
if test $SVN = yes; then
|
||||
echossh $1 "cd $2; svn up"
|
||||
echossh $1 "cd $2; $MAKE_CMD -f makeconf.mak.$$ configure"
|
||||
else
|
||||
# svn and autoconf locally
|
||||
echo "fake svn via svnexport, tar, autoconf, bison, flex."
|
||||
svn export svn+ssh://open.nlnetlabs.nl/svn/nsd/trunk unbound_ttt
|
||||
(cd unbound_ttt; $AC_CMD; rm -r autom4te* .c-mode-rc.el .cvsignore)
|
||||
if test $FIXCONFIGURE = yes; then
|
||||
echo fixing up configure length test.
|
||||
(cd unbound_ttt; mv configure oldconf; sed -e 's?while (test "X"?lt_cv_sys_max_cmd_len=65500; echo skip || while (test "X"?' <oldconf >configure; chmod +x ./configure)
|
||||
fi
|
||||
du unbound_ttt
|
||||
rsync -vrcpz --rsync-path=/home/wouter/bin/rsync unbound_ttt $1:unbound_ttt
|
||||
# tar czf unbound_ttt.tgz unbound_ttt
|
||||
rm -rf unbound_ttt
|
||||
# ls -al unbound_ttt.tgz
|
||||
# scp unbound_ttt.tgz $1:unbound_ttt.tar.gz
|
||||
# rm unbound_ttt.tgz
|
||||
# echossh $1 "gtar xzf unbound_ttt.tar.gz && rm unbound_ttt.tar.gz"
|
||||
fi
|
||||
echossh $1 "cd $2; $MAKE_CMD -f makeconf.mak.$$ Makefile"
|
||||
echossh $1 "cd $2; $MAKE_CMD all tests"
|
||||
echossh $1 "cd $2; $MAKE_CMD doc"
|
||||
if test $RUN_TEST = yes; then
|
||||
echossh $1 "cd $2; bash testcode/do-tests.sh"
|
||||
echossh $1 "cd $2/testdata; sh ../testcode/mini_tpkg.sh -q report" | tee -a $REPORT_FILE
|
||||
fi
|
||||
echossh $1 "cd $2; rm -f makeconf.mak.$$"
|
||||
rm -f makeconf.mak.$$
|
||||
echo "$1 end on "`date` | tee -a $REPORT_FILE
|
||||
}
|
||||
|
||||
echo "on "`date`" by $USER." > $REPORT_FILE
|
||||
echo "on "`date`" by $USER." > $LOG_FILE
|
||||
|
||||
# read host names
|
||||
declare -a hostname desc dir vars
|
||||
IFS=' '
|
||||
i=0
|
||||
while read a b c d; do
|
||||
if echo $a | grep "^#" >/dev/null; then
|
||||
continue # skip it
|
||||
fi
|
||||
# append after arrays
|
||||
hostname[$i]=$a
|
||||
desc[$i]=$b
|
||||
dir[$i]=$c
|
||||
vars[$i]=$d
|
||||
i=$(($i+1))
|
||||
done <$HOST_FILE
|
||||
echo "testing on $i hosts"
|
||||
|
||||
# do the test
|
||||
for((i=0; i<${#hostname[*]}; i=$i+1)); do
|
||||
if echo ${hostname[$i]} | grep "^#" >/dev/null; then
|
||||
continue # skip it
|
||||
fi
|
||||
# echo "hostname=[${hostname[$i]}]"
|
||||
# echo "desc=[${desc[$i]}]"
|
||||
# echo "dir=[${dir[$i]}]"
|
||||
# echo "vars=[${vars[$i]}]"
|
||||
AC_CMD="libtoolize -c --force; autoconf && autoheader"
|
||||
MAKE_CMD="make"
|
||||
SVN=yes
|
||||
IP6=yes
|
||||
FIXCONFIGURE=no
|
||||
RUN_TEST=yes
|
||||
LDNS=
|
||||
LIBEVENT=
|
||||
eval ${vars[$i]}
|
||||
echo "*** ${hostname[$i]} ${desc[$i]} ***" | tee -a $LOG_FILE | tee -a $REPORT_FILE
|
||||
dotest ${hostname[$i]} ${dir[$i]} 2>&1 | tee -a $LOG_FILE
|
||||
done
|
||||
|
||||
echo "done"
|
||||
38
external/unbound/testcode/testbed.txt
vendored
Normal file
38
external/unbound/testcode/testbed.txt
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
Testbed.sh help page.
|
||||
|
||||
Testbed helps in running the test packages (using tpkg(1)) on several systems.
|
||||
The script is specially written for unbound (edit it to change to different
|
||||
software). It is licensed BSD.
|
||||
|
||||
The hosts to run on are listed in host_file.<username>. You need to have
|
||||
public-key authorized ssh access to these systems (or type your password lots
|
||||
and lots of times). The host_file describes the directories and environment
|
||||
of each host. You need only user-level access to the host.
|
||||
|
||||
The host_file is very restrictive in formatting. Comments are lines starting
|
||||
with the # mark. The entries must be separated by tabs. Please list the
|
||||
hostname<tab>description<tab>checkoutdir<tab>variables
|
||||
|
||||
hostname: network hostname to ssh to.
|
||||
desc: pretty text to describe the machine architecture.
|
||||
checkoutdir: directory on the remote host where a svn checkout is present.
|
||||
variables: zero or more variables separated by spaces. BLA=value BAR=val.
|
||||
|
||||
Only important variable for unbound is the LDNS=<dir> variable that if present
|
||||
forces --with-ldns=<dir> to be passed to ./configure. In case LDNS is not
|
||||
installed on the system itself, but present somewhere else.
|
||||
|
||||
You can also set LIBEVENT=<dir> for the libevent directory, if it is
|
||||
installed in a nonstandard location.
|
||||
|
||||
*** Running the testbed
|
||||
|
||||
Run by executing the script. It will take all the hosts from the file in
|
||||
turn and update the svn directory there, possible autoreconf if necessary,
|
||||
possibly ./configure <args> if necessary, make the executables.
|
||||
Then it will run the testcode/do-tests script. This script should execute
|
||||
the tests that this host is capable of running.
|
||||
|
||||
in testdata/testbed.log has a line-by-line log. See your make errors here.
|
||||
in testdata/testbed.report has only the tpkg reports. Summary.
|
||||
|
||||
451
external/unbound/testcode/testbound.c
vendored
Normal file
451
external/unbound/testcode/testbound.c
vendored
Normal file
|
|
@ -0,0 +1,451 @@
|
|||
/*
|
||||
* testcode/testbound.c - test program for unbound.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* \file
|
||||
* Exits with code 1 on a failure. 0 if all unit tests are successfull.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#ifdef HAVE_TIME_H
|
||||
# include <time.h>
|
||||
#endif
|
||||
#include "testcode/testpkts.h"
|
||||
#include "testcode/replay.h"
|
||||
#include "testcode/fake_event.h"
|
||||
#include "daemon/remote.h"
|
||||
#include "util/config_file.h"
|
||||
#include "ldns/keyraw.h"
|
||||
#include <ctype.h>
|
||||
|
||||
/** signal that this is a testbound compile */
|
||||
#define unbound_testbound 1
|
||||
/**
|
||||
* include the main program from the unbound daemon.
|
||||
* rename main to daemon_main to call it
|
||||
*/
|
||||
#define main daemon_main
|
||||
#include "daemon/unbound.c"
|
||||
#undef main
|
||||
|
||||
/** maximum line length for lines in the replay file. */
|
||||
#define MAX_LINE_LEN 1024
|
||||
/** config files (removed at exit) */
|
||||
static struct config_strlist* cfgfiles = NULL;
|
||||
|
||||
/** give commandline usage for testbound. */
|
||||
static void
|
||||
testbound_usage()
|
||||
{
|
||||
printf("usage: testbound [options]\n");
|
||||
printf("\ttest the unbound daemon.\n");
|
||||
printf("-h this help\n");
|
||||
printf("-p file playback text file\n");
|
||||
printf("-2 detect SHA256 support (exit code 0 or 1)\n");
|
||||
printf("-g detect GOST support (exit code 0 or 1)\n");
|
||||
printf("-e detect ECDSA support (exit code 0 or 1)\n");
|
||||
printf("-s testbound self-test - unit test of testbound parts.\n");
|
||||
printf("-o str unbound commandline options separated by spaces.\n");
|
||||
printf("Version %s\n", PACKAGE_VERSION);
|
||||
printf("BSD licensed, see LICENSE file in source package.\n");
|
||||
printf("Report bugs to %s.\n", PACKAGE_BUGREPORT);
|
||||
}
|
||||
|
||||
/** Max number of arguments to pass to unbound. */
|
||||
#define MAXARG 100
|
||||
|
||||
/**
|
||||
* Add options from string to passed argc. splits on whitespace.
|
||||
* @param args: the option argument, "-v -p 12345" or so.
|
||||
* @param pass_argc: ptr to the argc for unbound. Modified.
|
||||
* @param pass_argv: the argv to pass to unbound. Modified.
|
||||
*/
|
||||
static void
|
||||
add_opts(const char* args, int* pass_argc, char* pass_argv[])
|
||||
{
|
||||
const char *p = args, *np;
|
||||
size_t len;
|
||||
while(p && isspace((int)*p))
|
||||
p++;
|
||||
while(p && *p) {
|
||||
/* find location of next string and length of this one */
|
||||
if((np = strchr(p, ' ')))
|
||||
len = (size_t)(np-p);
|
||||
else len = strlen(p);
|
||||
/* allocate and copy option */
|
||||
if(*pass_argc >= MAXARG-1)
|
||||
fatal_exit("too many arguments: '%s'", p);
|
||||
pass_argv[*pass_argc] = (char*)malloc(len+1);
|
||||
if(!pass_argv[*pass_argc])
|
||||
fatal_exit("add_opts: out of memory");
|
||||
memcpy(pass_argv[*pass_argc], p, len);
|
||||
pass_argv[*pass_argc][len] = 0;
|
||||
(*pass_argc)++;
|
||||
/* go to next option */
|
||||
p = np;
|
||||
while(p && isspace((int)*p))
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
/** pretty print commandline for unbound in this test */
|
||||
static void
|
||||
echo_cmdline(int argc, char* argv[])
|
||||
{
|
||||
int i;
|
||||
fprintf(stderr, "testbound is starting:");
|
||||
for(i=0; i<argc; i++) {
|
||||
fprintf(stderr, " [%s]", argv[i]);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
/** spool autotrust file */
|
||||
static void
|
||||
spool_auto_file(FILE* in, int* lineno, FILE* cfg, char* id)
|
||||
{
|
||||
char line[MAX_LINE_LEN];
|
||||
char* parse;
|
||||
FILE* spool;
|
||||
/* find filename for new file */
|
||||
while(isspace((int)*id))
|
||||
id++;
|
||||
if(strlen(id)==0)
|
||||
fatal_exit("AUTROTRUST_FILE must have id, line %d", *lineno);
|
||||
id[strlen(id)-1]=0; /* remove newline */
|
||||
fake_temp_file("_auto_", id, line, sizeof(line));
|
||||
/* add option for the file */
|
||||
fprintf(cfg, "server: auto-trust-anchor-file: \"%s\"\n", line);
|
||||
/* open file and spool to it */
|
||||
spool = fopen(line, "w");
|
||||
if(!spool) fatal_exit("could not open %s: %s", line, strerror(errno));
|
||||
fprintf(stderr, "testbound is spooling key file: %s\n", line);
|
||||
if(!cfg_strlist_insert(&cfgfiles, strdup(line)))
|
||||
fatal_exit("out of memory");
|
||||
line[sizeof(line)-1] = 0;
|
||||
while(fgets(line, MAX_LINE_LEN-1, in)) {
|
||||
parse = line;
|
||||
(*lineno)++;
|
||||
while(isspace((int)*parse))
|
||||
parse++;
|
||||
if(strncmp(parse, "AUTOTRUST_END", 13) == 0) {
|
||||
fclose(spool);
|
||||
return;
|
||||
}
|
||||
fputs(line, spool);
|
||||
}
|
||||
fatal_exit("no AUTOTRUST_END in input file");
|
||||
}
|
||||
|
||||
/** process config elements */
|
||||
static void
|
||||
setup_config(FILE* in, int* lineno, int* pass_argc, char* pass_argv[])
|
||||
{
|
||||
char configfile[MAX_LINE_LEN];
|
||||
char line[MAX_LINE_LEN];
|
||||
char* parse;
|
||||
FILE* cfg;
|
||||
fake_temp_file("_cfg", "", configfile, sizeof(configfile));
|
||||
add_opts("-c", pass_argc, pass_argv);
|
||||
add_opts(configfile, pass_argc, pass_argv);
|
||||
cfg = fopen(configfile, "w");
|
||||
if(!cfg) fatal_exit("could not open %s: %s",
|
||||
configfile, strerror(errno));
|
||||
if(!cfg_strlist_insert(&cfgfiles, strdup(configfile)))
|
||||
fatal_exit("out of memory");
|
||||
line[sizeof(line)-1] = 0;
|
||||
/* some basic settings to not pollute the host system */
|
||||
fprintf(cfg, "server: use-syslog: no\n");
|
||||
fprintf(cfg, " directory: \"\"\n");
|
||||
fprintf(cfg, " chroot: \"\"\n");
|
||||
fprintf(cfg, " username: \"\"\n");
|
||||
fprintf(cfg, " pidfile: \"\"\n");
|
||||
fprintf(cfg, " val-log-level: 2\n");
|
||||
fprintf(cfg, "remote-control: control-enable: no\n");
|
||||
while(fgets(line, MAX_LINE_LEN-1, in)) {
|
||||
parse = line;
|
||||
(*lineno)++;
|
||||
while(isspace((int)*parse))
|
||||
parse++;
|
||||
if(!*parse || parse[0] == ';')
|
||||
continue;
|
||||
if(strncmp(parse, "COMMANDLINE", 11) == 0) {
|
||||
parse[strlen(parse)-1] = 0; /* strip off \n */
|
||||
add_opts(parse+11, pass_argc, pass_argv);
|
||||
continue;
|
||||
}
|
||||
if(strncmp(parse, "AUTOTRUST_FILE", 14) == 0) {
|
||||
spool_auto_file(in, lineno, cfg, parse+14);
|
||||
continue;
|
||||
}
|
||||
if(strncmp(parse, "CONFIG_END", 10) == 0) {
|
||||
fclose(cfg);
|
||||
return;
|
||||
}
|
||||
fputs(line, cfg);
|
||||
}
|
||||
fatal_exit("No CONFIG_END in input file");
|
||||
|
||||
}
|
||||
|
||||
/** read playback file */
|
||||
static struct replay_scenario*
|
||||
setup_playback(const char* filename, int* pass_argc, char* pass_argv[])
|
||||
{
|
||||
struct replay_scenario* scen = NULL;
|
||||
int lineno = 0;
|
||||
|
||||
if(filename) {
|
||||
FILE *in = fopen(filename, "rb");
|
||||
if(!in) {
|
||||
perror(filename);
|
||||
exit(1);
|
||||
}
|
||||
setup_config(in, &lineno, pass_argc, pass_argv);
|
||||
scen = replay_scenario_read(in, filename, &lineno);
|
||||
fclose(in);
|
||||
if(!scen)
|
||||
fatal_exit("Could not read: %s", filename);
|
||||
}
|
||||
else fatal_exit("need a playback file (-p)");
|
||||
log_info("Scenario: %s", scen->title);
|
||||
return scen;
|
||||
}
|
||||
|
||||
/** remove config file at exit */
|
||||
void remove_configfile(void)
|
||||
{
|
||||
struct config_strlist* p;
|
||||
for(p=cfgfiles; p; p=p->next)
|
||||
unlink(p->str);
|
||||
config_delstrlist(cfgfiles);
|
||||
cfgfiles = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main fake event test program. Setup, teardown and report errors.
|
||||
* @param argc: arg count.
|
||||
* @param argv: array of commandline arguments.
|
||||
* @return program failure if test fails.
|
||||
*/
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
int c, res;
|
||||
int pass_argc = 0;
|
||||
char* pass_argv[MAXARG];
|
||||
char* playback_file = NULL;
|
||||
int init_optind = optind;
|
||||
char* init_optarg = optarg;
|
||||
struct replay_scenario* scen = NULL;
|
||||
|
||||
/* we do not want the test to depend on the timezone */
|
||||
(void)putenv("TZ=UTC");
|
||||
|
||||
log_init(NULL, 0, NULL);
|
||||
/* determine commandline options for the daemon */
|
||||
pass_argc = 1;
|
||||
pass_argv[0] = "unbound";
|
||||
add_opts("-d", &pass_argc, pass_argv);
|
||||
while( (c=getopt(argc, argv, "2egho:p:s")) != -1) {
|
||||
switch(c) {
|
||||
case 's':
|
||||
free(pass_argv[1]);
|
||||
testbound_selftest();
|
||||
printf("selftest successful\n");
|
||||
exit(0);
|
||||
case '2':
|
||||
#if (defined(HAVE_EVP_SHA256) || defined(HAVE_NSS)) && defined(USE_SHA2)
|
||||
printf("SHA256 supported\n");
|
||||
exit(0);
|
||||
#else
|
||||
printf("SHA256 not supported\n");
|
||||
exit(1);
|
||||
#endif
|
||||
break;
|
||||
case 'e':
|
||||
#if defined(USE_ECDSA)
|
||||
printf("ECDSA supported\n");
|
||||
exit(0);
|
||||
#else
|
||||
printf("ECDSA not supported\n");
|
||||
exit(1);
|
||||
#endif
|
||||
break;
|
||||
case 'g':
|
||||
#ifdef USE_GOST
|
||||
if(sldns_key_EVP_load_gost_id()) {
|
||||
printf("GOST supported\n");
|
||||
exit(0);
|
||||
} else {
|
||||
printf("GOST not supported\n");
|
||||
exit(1);
|
||||
}
|
||||
#else
|
||||
printf("GOST not supported\n");
|
||||
exit(1);
|
||||
#endif
|
||||
break;
|
||||
case 'p':
|
||||
playback_file = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
add_opts(optarg, &pass_argc, pass_argv);
|
||||
break;
|
||||
case '?':
|
||||
case 'h':
|
||||
default:
|
||||
testbound_usage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if(argc != 0) {
|
||||
testbound_usage();
|
||||
return 1;
|
||||
}
|
||||
log_info("Start of %s testbound program.", PACKAGE_STRING);
|
||||
if(atexit(&remove_configfile) != 0)
|
||||
fatal_exit("atexit() failed: %s", strerror(errno));
|
||||
|
||||
/* setup test environment */
|
||||
scen = setup_playback(playback_file, &pass_argc, pass_argv);
|
||||
/* init fake event backend */
|
||||
fake_event_init(scen);
|
||||
|
||||
pass_argv[pass_argc] = NULL;
|
||||
echo_cmdline(pass_argc, pass_argv);
|
||||
|
||||
/* reset getopt processing */
|
||||
optind = init_optind;
|
||||
optarg = init_optarg;
|
||||
|
||||
/* run the normal daemon */
|
||||
res = daemon_main(pass_argc, pass_argv);
|
||||
|
||||
fake_event_cleanup();
|
||||
for(c=1; c<pass_argc; c++)
|
||||
free(pass_argv[c]);
|
||||
if(res == 0) {
|
||||
log_info("Testbound Exit Success");
|
||||
#ifdef HAVE_PTHREAD
|
||||
/* dlopen frees its thread state (dlopen of gost engine) */
|
||||
pthread_exit(NULL);
|
||||
#endif
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* fake remote control */
|
||||
struct listen_port* daemon_remote_open_ports(struct config_file*
|
||||
ATTR_UNUSED(cfg))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct daemon_remote* daemon_remote_create(struct config_file* ATTR_UNUSED(cfg))
|
||||
{
|
||||
return (struct daemon_remote*)calloc(1,1);
|
||||
}
|
||||
|
||||
void daemon_remote_delete(struct daemon_remote* rc)
|
||||
{
|
||||
free(rc);
|
||||
}
|
||||
|
||||
void daemon_remote_clear(struct daemon_remote* ATTR_UNUSED(rc))
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
int daemon_remote_open_accept(struct daemon_remote* ATTR_UNUSED(rc),
|
||||
struct listen_port* ATTR_UNUSED(ports),
|
||||
struct worker* ATTR_UNUSED(worker))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int remote_accept_callback(struct comm_point* ATTR_UNUSED(c),
|
||||
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
|
||||
struct comm_reply* ATTR_UNUSED(repinfo))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int remote_control_callback(struct comm_point* ATTR_UNUSED(c),
|
||||
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
|
||||
struct comm_reply* ATTR_UNUSED(repinfo))
|
||||
{
|
||||
log_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void remote_get_opt_ssl(char* ATTR_UNUSED(str), void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void wsvc_command_option(const char* ATTR_UNUSED(wopt),
|
||||
const char* ATTR_UNUSED(cfgfile), int ATTR_UNUSED(v),
|
||||
int ATTR_UNUSED(c))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void wsvc_setup_worker(struct worker* ATTR_UNUSED(worker))
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
void wsvc_desetup_worker(struct worker* ATTR_UNUSED(worker))
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
#ifdef UB_ON_WINDOWS
|
||||
void worker_win_stop_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
|
||||
void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
|
||||
void wsvc_cron_cb(void* ATTR_UNUSED(arg))
|
||||
{
|
||||
log_assert(0);
|
||||
}
|
||||
#endif /* UB_ON_WINDOWS */
|
||||
|
||||
1427
external/unbound/testcode/testpkts.c
vendored
Normal file
1427
external/unbound/testcode/testpkts.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
268
external/unbound/testcode/testpkts.h
vendored
Normal file
268
external/unbound/testcode/testpkts.h
vendored
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* testpkts. Data file parse for test packets, and query matching.
|
||||
*
|
||||
* Data storage for specially crafted replies for testing purposes.
|
||||
*
|
||||
* (c) NLnet Labs, 2005, 2006, 2007
|
||||
* See the file LICENSE for the license
|
||||
*/
|
||||
|
||||
#ifndef TESTPKTS_H
|
||||
#define TESTPKTS_H
|
||||
struct sldns_buffer;
|
||||
struct sldns_file_parse_state;
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This is a debugging aid. It is not efficient, especially
|
||||
* with a long config file, but it can give any reply to any query.
|
||||
* This can help the developer pre-script replies for queries.
|
||||
*
|
||||
* You can specify a packet RR by RR with header flags to return.
|
||||
*
|
||||
* Missing features:
|
||||
* - matching content different from reply content.
|
||||
* - find way to adjust mangled packets?
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
The data file format is as follows:
|
||||
|
||||
; comment.
|
||||
; a number of entries, these are processed first to last.
|
||||
; a line based format.
|
||||
|
||||
$ORIGIN origin
|
||||
$TTL default_ttl
|
||||
|
||||
ENTRY_BEGIN
|
||||
; first give MATCH lines, that say what queries are matched
|
||||
; by this entry.
|
||||
; 'opcode' makes the query match the opcode from the reply
|
||||
; if you leave it out, any opcode matches this entry.
|
||||
; 'qtype' makes the query match the qtype from the reply
|
||||
; 'qname' makes the query match the qname from the reply
|
||||
; 'subdomain' makes the query match subdomains of qname from the reply
|
||||
; 'serial=1023' makes the query match if ixfr serial is 1023.
|
||||
; 'all' has to match header byte for byte and all rrs in packet.
|
||||
; 'ttl' used with all, rrs in packet must also have matching TTLs.
|
||||
; 'DO' will match only queries with DO bit set.
|
||||
; 'noedns' matches queries without EDNS OPT records.
|
||||
MATCH [opcode] [qtype] [qname] [serial=<value>] [all] [ttl]
|
||||
MATCH [UDP|TCP] DO
|
||||
MATCH ...
|
||||
; Then the REPLY header is specified.
|
||||
REPLY opcode, rcode or flags.
|
||||
(opcode) QUERY IQUERY STATUS NOTIFY UPDATE
|
||||
(rcode) NOERROR FORMERR SERVFAIL NXDOMAIN NOTIMPL YXDOMAIN
|
||||
YXRRSET NXRRSET NOTAUTH NOTZONE
|
||||
(flags) QR AA TC RD CD RA AD DO
|
||||
REPLY ...
|
||||
; any additional actions to do.
|
||||
; 'copy_id' copies the ID from the query to the answer.
|
||||
ADJUST copy_id
|
||||
; 'copy_query' copies the query name, type and class to the answer.
|
||||
ADJUST copy_query
|
||||
; 'sleep=10' sleeps for 10 seconds before giving the answer (TCP is open)
|
||||
ADJUST [sleep=<num>] ; sleep before giving any reply
|
||||
ADJUST [packet_sleep=<num>] ; sleep before this packet in sequence
|
||||
SECTION QUESTION
|
||||
<RRs, one per line> ; the RRcount is determined automatically.
|
||||
SECTION ANSWER
|
||||
<RRs, one per line>
|
||||
SECTION AUTHORITY
|
||||
<RRs, one per line>
|
||||
SECTION ADDITIONAL
|
||||
<RRs, one per line>
|
||||
EXTRA_PACKET ; follow with SECTION, REPLY for more packets.
|
||||
HEX_ANSWER_BEGIN ; follow with hex data
|
||||
; this replaces any answer packet constructed
|
||||
; with the SECTION keywords (only SECTION QUERY
|
||||
; is used to match queries). If the data cannot
|
||||
; be parsed, ADJUST rules for the answer packet
|
||||
; are ignored. Only copy_id is done.
|
||||
HEX_ANSWER_END
|
||||
ENTRY_END
|
||||
|
||||
|
||||
Example data file:
|
||||
$ORIGIN nlnetlabs.nl
|
||||
$TTL 3600
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH qname
|
||||
REPLY NOERROR
|
||||
ADJUST copy_id
|
||||
SECTION QUESTION
|
||||
www.nlnetlabs.nl. IN A
|
||||
SECTION ANSWER
|
||||
www.nlnetlabs.nl. IN A 195.169.215.155
|
||||
SECTION AUTHORITY
|
||||
nlnetlabs.nl. IN NS www.nlnetlabs.nl.
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH qname
|
||||
REPLY NOERROR
|
||||
ADJUST copy_id
|
||||
SECTION QUESTION
|
||||
www2.nlnetlabs.nl. IN A
|
||||
HEX_ANSWER_BEGIN
|
||||
; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
||||
;-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||
00 bf 81 80 00 01 00 01 00 02 00 02 03 77 77 77 0b 6b 61 6e ; 1- 20
|
||||
61 72 69 65 70 69 65 74 03 63 6f 6d 00 00 01 00 01 03 77 77 ; 21- 40
|
||||
77 0b 6b 61 6e 61 72 69 65 70 69 65 74 03 63 6f 6d 00 00 01 ; 41- 60
|
||||
00 01 00 01 50 8b 00 04 52 5e ed 32 0b 6b 61 6e 61 72 69 65 ; 61- 80
|
||||
70 69 65 74 03 63 6f 6d 00 00 02 00 01 00 01 50 8b 00 11 03 ; 81- 100
|
||||
6e 73 31 08 68 65 78 6f 6e 2d 69 73 02 6e 6c 00 0b 6b 61 6e ; 101- 120
|
||||
61 72 69 65 70 69 65 74 03 63 6f 6d 00 00 02 00 01 00 01 50 ; 121- 140
|
||||
8b 00 11 03 6e 73 32 08 68 65 78 6f 6e 2d 69 73 02 6e 6c 00 ; 141- 160
|
||||
03 6e 73 31 08 68 65 78 6f 6e 2d 69 73 02 6e 6c 00 00 01 00 ; 161- 180
|
||||
01 00 00 46 53 00 04 52 5e ed 02 03 6e 73 32 08 68 65 78 6f ; 181- 200
|
||||
6e 2d 69 73 02 6e 6c 00 00 01 00 01 00 00 46 53 00 04 d4 cc ; 201- 220
|
||||
db 5b
|
||||
HEX_ANSWER_END
|
||||
ENTRY_END
|
||||
|
||||
|
||||
|
||||
note that this file will link with your
|
||||
void verbose(int level, char* format, ...); output function.
|
||||
*/
|
||||
|
||||
/** Type of transport, since some entries match based on UDP or TCP of query */
|
||||
enum transport_type {transport_any = 0, transport_udp, transport_tcp };
|
||||
|
||||
/** struct to keep a linked list of reply packets for a query */
|
||||
struct reply_packet {
|
||||
/** next in list of reply packets, for TCP multiple pkts on wire */
|
||||
struct reply_packet* next;
|
||||
/** the reply pkt */
|
||||
uint8_t* reply_pkt;
|
||||
/** length of reply pkt */
|
||||
size_t reply_len;
|
||||
/** or reply pkt in hex if not parsable */
|
||||
struct sldns_buffer* reply_from_hex;
|
||||
/** seconds to sleep before giving packet */
|
||||
unsigned int packet_sleep;
|
||||
};
|
||||
|
||||
/** data structure to keep the canned queries in.
|
||||
format is the 'matching query' and the 'canned answer' */
|
||||
struct entry {
|
||||
/* match */
|
||||
/* How to match an incoming query with this canned reply */
|
||||
/** match query opcode with answer opcode */
|
||||
uint8_t match_opcode;
|
||||
/** match qtype with answer qtype */
|
||||
uint8_t match_qtype;
|
||||
/** match qname with answer qname */
|
||||
uint8_t match_qname;
|
||||
/** match qname as subdomain of answer qname */
|
||||
uint8_t match_subdomain;
|
||||
/** match SOA serial number, from auth section */
|
||||
uint8_t match_serial;
|
||||
/** match all of the packet */
|
||||
uint8_t match_all;
|
||||
/** match ttls in the packet */
|
||||
uint8_t match_ttl;
|
||||
/** match DO bit */
|
||||
uint8_t match_do;
|
||||
/** match absence of EDNS OPT record in query */
|
||||
uint8_t match_noedns;
|
||||
/** match query serial with this value. */
|
||||
uint32_t ixfr_soa_serial;
|
||||
/** match on UDP/TCP */
|
||||
enum transport_type match_transport;
|
||||
|
||||
/** pre canned reply */
|
||||
struct reply_packet *reply_list;
|
||||
|
||||
/** how to adjust the reply packet */
|
||||
/** copy over the ID from the query into the answer */
|
||||
uint8_t copy_id;
|
||||
/** copy the query nametypeclass from query into the answer */
|
||||
uint8_t copy_query;
|
||||
/** in seconds */
|
||||
unsigned int sleeptime;
|
||||
|
||||
/** some number that names this entry, line number in file or so */
|
||||
int lineno;
|
||||
|
||||
/** next in list */
|
||||
struct entry* next;
|
||||
};
|
||||
|
||||
/**
|
||||
* reads the canned reply file and returns a list of structs
|
||||
* does an exit on error.
|
||||
* @param name: name of the file to read.
|
||||
* @param skip_whitespace: skip leftside whitespace.
|
||||
*/
|
||||
struct entry* read_datafile(const char* name, int skip_whitespace);
|
||||
|
||||
/**
|
||||
* Delete linked list of entries.
|
||||
*/
|
||||
void delete_entry(struct entry* list);
|
||||
|
||||
/**
|
||||
* Read one entry from the data file.
|
||||
* @param in: file to read from. Filepos must be at the start of a new line.
|
||||
* @param name: name of the file for prettier errors.
|
||||
* @param pstate: file parse state with lineno, default_ttl,
|
||||
* oirigin and prev_rr name.
|
||||
* @param skip_whitespace: skip leftside whitespace.
|
||||
* @return: The entry read (malloced) or NULL if no entry could be read.
|
||||
*/
|
||||
struct entry* read_entry(FILE* in, const char* name,
|
||||
struct sldns_file_parse_state* pstate, int skip_whitespace);
|
||||
|
||||
/**
|
||||
* finds entry in list, or returns NULL.
|
||||
*/
|
||||
struct entry* find_match(struct entry* entries, uint8_t* query_pkt,
|
||||
size_t query_pkt_len, enum transport_type transport);
|
||||
|
||||
/**
|
||||
* match two packets, all must match
|
||||
* @param q: packet 1
|
||||
* @param qlen: length of q.
|
||||
* @param p: packet 2
|
||||
* @param plen: length of p.
|
||||
* @param mttl: if true, ttls must match, if false, ttls do not need to match
|
||||
* @param noloc: if true, rrs may be reordered in their packet-section.
|
||||
* rrs are then matches without location of the rr being important.
|
||||
* @return true if matched.
|
||||
*/
|
||||
int match_all(uint8_t* q, size_t qlen, uint8_t* p, size_t plen, int mttl,
|
||||
int noloc);
|
||||
|
||||
/**
|
||||
* copy & adjust packet, mallocs a copy.
|
||||
*/
|
||||
void adjust_packet(struct entry* match, uint8_t** answer_pkt,
|
||||
size_t* answer_pkt_len, uint8_t* query_pkt, size_t query_pkt_len);
|
||||
|
||||
/**
|
||||
* Parses data buffer to a query, finds the correct answer
|
||||
* and calls the given function for every packet to send.
|
||||
* if verbose_out filename is given, packets are dumped there.
|
||||
* @param inbuf: the packet that came in
|
||||
* @param inlen: length of packet.
|
||||
* @param entries: entries read in from datafile.
|
||||
* @param count: is increased to count number of queries answered.
|
||||
* @param transport: set to UDP or TCP to match some types of entries.
|
||||
* @param sendfunc: called to send answer (buffer, size, userarg).
|
||||
* @param userdata: userarg to give to sendfunc.
|
||||
* @param verbose_out: if not NULL, verbose messages are printed there.
|
||||
*/
|
||||
void handle_query(uint8_t* inbuf, ssize_t inlen, struct entry* entries,
|
||||
int* count, enum transport_type transport,
|
||||
void (*sendfunc)(uint8_t*, size_t, void*), void* userdata,
|
||||
FILE* verbose_out);
|
||||
|
||||
#endif /* TESTPKTS_H */
|
||||
137
external/unbound/testcode/unitanchor.c
vendored
Normal file
137
external/unbound/testcode/unitanchor.c
vendored
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* testcode/unitanchor.c - unit test for trust anchor storage.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* \file
|
||||
* Calls trust anchor unit tests. Exits with code 1 on a failure.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "testcode/unitmain.h"
|
||||
#include "validator/val_anchor.h"
|
||||
#include "ldns/sbuffer.h"
|
||||
#include "ldns/rrdef.h"
|
||||
|
||||
/** test empty set */
|
||||
static void
|
||||
test_anchor_empty(struct val_anchors* a)
|
||||
{
|
||||
uint16_t c = LDNS_RR_CLASS_IN;
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\000", 1, c) == NULL);
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\003com\000", 5, c) == NULL);
|
||||
unit_assert(anchors_lookup(a,
|
||||
(uint8_t*)"\007example\003com\000", 11, c) == NULL);
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\002nl\000", 4, c) == NULL);
|
||||
unit_assert(anchors_lookup(a,
|
||||
(uint8_t*)"\004labs\002nl\000", 9, c) == NULL);
|
||||
unit_assert(anchors_lookup(a,
|
||||
(uint8_t*)"\004fabs\002nl\000", 9, c) == NULL);
|
||||
}
|
||||
|
||||
/** test set of one anchor */
|
||||
static void
|
||||
test_anchor_one(sldns_buffer* buff, struct val_anchors* a)
|
||||
{
|
||||
struct trust_anchor* ta;
|
||||
uint16_t c = LDNS_RR_CLASS_IN;
|
||||
unit_assert(anchor_store_str(a, buff,
|
||||
"nl. DS 42860 5 1 14D739EB566D2B1A5E216A0BA4D17FA9B038BE4A"));
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\000", 1, c) == NULL);
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\003com\000", 5, c) == NULL);
|
||||
unit_assert(anchors_lookup(a,
|
||||
(uint8_t*)"\007example\003com\000", 11, c) == NULL);
|
||||
|
||||
unit_assert((ta=anchors_lookup(a,
|
||||
(uint8_t*)"\002nl\000", 4, c)) != NULL);
|
||||
lock_basic_unlock(&ta->lock);
|
||||
|
||||
unit_assert((ta=anchors_lookup(a,
|
||||
(uint8_t*)"\004labs\002nl\000", 9, c)) != NULL);
|
||||
lock_basic_unlock(&ta->lock);
|
||||
|
||||
unit_assert((ta=anchors_lookup(a,
|
||||
(uint8_t*)"\004fabs\002nl\000", 9, c)) != NULL);
|
||||
lock_basic_unlock(&ta->lock);
|
||||
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\002oo\000", 4, c) == NULL);
|
||||
}
|
||||
|
||||
/** test with several anchors */
|
||||
static void
|
||||
test_anchors(sldns_buffer* buff, struct val_anchors* a)
|
||||
{
|
||||
struct trust_anchor* ta;
|
||||
uint16_t c = LDNS_RR_CLASS_IN;
|
||||
unit_assert(anchor_store_str(a, buff,
|
||||
"labs.nl. DS 42860 5 1 14D739EB566D2B1A5E216A0BA4D17FA9B038BE4A"));
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\000", 1, c) == NULL);
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\003com\000", 5, c) == NULL);
|
||||
unit_assert(anchors_lookup(a,
|
||||
(uint8_t*)"\007example\003com\000", 11, c) == NULL);
|
||||
|
||||
unit_assert(ta = anchors_lookup(a, (uint8_t*)"\002nl\000", 4, c));
|
||||
unit_assert(query_dname_compare(ta->name, (uint8_t*)"\002nl\000")==0);
|
||||
lock_basic_unlock(&ta->lock);
|
||||
|
||||
unit_assert(ta = anchors_lookup(a,
|
||||
(uint8_t*)"\004labs\002nl\000", 9, c));
|
||||
unit_assert(query_dname_compare(ta->name,
|
||||
(uint8_t*)"\004labs\002nl\000") == 0);
|
||||
lock_basic_unlock(&ta->lock);
|
||||
|
||||
unit_assert(ta = anchors_lookup(a,
|
||||
(uint8_t*)"\004fabs\002nl\000", 9, c));
|
||||
unit_assert(query_dname_compare(ta->name,
|
||||
(uint8_t*)"\002nl\000") == 0);
|
||||
lock_basic_unlock(&ta->lock);
|
||||
|
||||
unit_assert(anchors_lookup(a, (uint8_t*)"\002oo\000", 4, c) == NULL);
|
||||
}
|
||||
|
||||
void anchors_test(void)
|
||||
{
|
||||
sldns_buffer* buff = sldns_buffer_new(65800);
|
||||
struct val_anchors* a;
|
||||
unit_show_feature("trust anchor store");
|
||||
unit_assert(a = anchors_create());
|
||||
sldns_buffer_flip(buff);
|
||||
test_anchor_empty(a);
|
||||
test_anchor_one(buff, a);
|
||||
test_anchors(buff, a);
|
||||
anchors_delete(a);
|
||||
sldns_buffer_free(buff);
|
||||
}
|
||||
861
external/unbound/testcode/unitdname.c
vendored
Normal file
861
external/unbound/testcode/unitdname.c
vendored
Normal file
|
|
@ -0,0 +1,861 @@
|
|||
/*
|
||||
* testcode/unitdname.c - unit test for dname routines.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* \file
|
||||
* Calls dname unit tests. Exits with code 1 on a failure.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "testcode/unitmain.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "ldns/sbuffer.h"
|
||||
#include "ldns/str2wire.h"
|
||||
|
||||
/** put dname into buffer */
|
||||
static sldns_buffer*
|
||||
dname_to_buf(sldns_buffer* b, const char* str)
|
||||
{
|
||||
int e;
|
||||
size_t len = sldns_buffer_capacity(b);
|
||||
sldns_buffer_clear(b);
|
||||
e = sldns_str2wire_dname_buf(str, sldns_buffer_begin(b), &len);
|
||||
if(e != 0)
|
||||
fatal_exit("%s ldns: %s", __func__,
|
||||
sldns_get_errorstr_parse(e));
|
||||
sldns_buffer_set_position(b, len);
|
||||
sldns_buffer_flip(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
/** test query_dname_len function */
|
||||
static void
|
||||
dname_test_qdl(sldns_buffer* buff)
|
||||
{
|
||||
unit_show_func("util/data/dname.c", "query_dname_len");
|
||||
unit_assert( query_dname_len(buff) == 0);
|
||||
unit_assert( query_dname_len(dname_to_buf(buff, ".")) == 1 );
|
||||
unit_assert( query_dname_len(dname_to_buf(buff, "bla.foo.")) == 9 );
|
||||
unit_assert( query_dname_len(dname_to_buf(buff, "x.y.z.example.com."
|
||||
)) == 19 );
|
||||
}
|
||||
|
||||
/** test query_dname_tolower */
|
||||
static void
|
||||
dname_test_qdtl(sldns_buffer* buff)
|
||||
{
|
||||
unit_show_func("util/data/dname.c", "query_dname_tolower");
|
||||
sldns_buffer_write_at(buff, 0, "\012abCDeaBCde\003cOm\000", 16);
|
||||
query_dname_tolower(sldns_buffer_begin(buff));
|
||||
unit_assert( memcmp(sldns_buffer_begin(buff),
|
||||
"\012abcdeabcde\003com\000", 16) == 0);
|
||||
|
||||
sldns_buffer_write_at(buff, 0, "\001+\012abC{e-ZYXe\003NET\000", 18);
|
||||
query_dname_tolower(sldns_buffer_begin(buff));
|
||||
unit_assert( memcmp(sldns_buffer_begin(buff),
|
||||
"\001+\012abc{e-zyxe\003net\000", 18) == 0);
|
||||
|
||||
sldns_buffer_write_at(buff, 0, "\000", 1);
|
||||
query_dname_tolower(sldns_buffer_begin(buff));
|
||||
unit_assert( memcmp(sldns_buffer_begin(buff), "\000", 1) == 0);
|
||||
|
||||
sldns_buffer_write_at(buff, 0, "\002NL\000", 4);
|
||||
query_dname_tolower(sldns_buffer_begin(buff));
|
||||
unit_assert( memcmp(sldns_buffer_begin(buff), "\002nl\000", 4) == 0);
|
||||
}
|
||||
|
||||
/** test query_dname_compare */
|
||||
static void
|
||||
dname_test_query_dname_compare(void)
|
||||
{
|
||||
unit_show_func("util/data/dname.c", "query_dname_compare");
|
||||
unit_assert(query_dname_compare((uint8_t*)"", (uint8_t*)"") == 0);
|
||||
unit_assert(query_dname_compare((uint8_t*)"\001a",
|
||||
(uint8_t*)"\001a") == 0);
|
||||
unit_assert(query_dname_compare((uint8_t*)"\003abc\001a",
|
||||
(uint8_t*)"\003abc\001a") == 0);
|
||||
unit_assert(query_dname_compare((uint8_t*)"\003aBc\001a",
|
||||
(uint8_t*)"\003AbC\001A") == 0);
|
||||
unit_assert(query_dname_compare((uint8_t*)"\003abc",
|
||||
(uint8_t*)"\003abc\001a") == -1);
|
||||
unit_assert(query_dname_compare((uint8_t*)"\003abc\001a",
|
||||
(uint8_t*)"\003abc") == +1);
|
||||
unit_assert(query_dname_compare((uint8_t*)"\003abc\001a",
|
||||
(uint8_t*)"") == +1);
|
||||
unit_assert(query_dname_compare((uint8_t*)"",
|
||||
(uint8_t*)"\003abc\001a") == -1);
|
||||
unit_assert(query_dname_compare((uint8_t*)"\003abc\001a",
|
||||
(uint8_t*)"\003xxx\001a") == -1);
|
||||
unit_assert(query_dname_compare((uint8_t*)"\003axx\001a",
|
||||
(uint8_t*)"\003abc\001a") == 1);
|
||||
unit_assert(query_dname_compare((uint8_t*)"\003abc\001a",
|
||||
(uint8_t*)"\003abc\001Z") == -1);
|
||||
unit_assert(query_dname_compare((uint8_t*)"\003abc\001Z",
|
||||
(uint8_t*)"\003abc\001a") == 1);
|
||||
}
|
||||
|
||||
/** test dname_count_labels */
|
||||
static void
|
||||
dname_test_count_labels(void)
|
||||
{
|
||||
unit_show_func("util/data/dname.c", "dname_count_labels");
|
||||
unit_assert(dname_count_labels((uint8_t*)"") == 1);
|
||||
unit_assert(dname_count_labels((uint8_t*)"\003com") == 2);
|
||||
unit_assert(dname_count_labels((uint8_t*)"\003org") == 2);
|
||||
unit_assert(dname_count_labels((uint8_t*)"\007example\003com") == 3);
|
||||
unit_assert(dname_count_labels((uint8_t*)"\003bla\007example\003com")
|
||||
== 4);
|
||||
}
|
||||
|
||||
/** test dname_count_size_labels */
|
||||
static void
|
||||
dname_test_count_size_labels(void)
|
||||
{
|
||||
size_t sz = 0;
|
||||
unit_show_func("util/data/dname.c", "dname_count_size_labels");
|
||||
unit_assert(dname_count_size_labels((uint8_t*)"", &sz) == 1);
|
||||
unit_assert(sz == 1);
|
||||
unit_assert(dname_count_size_labels((uint8_t*)"\003com", &sz) == 2);
|
||||
unit_assert(sz == 5);
|
||||
unit_assert(dname_count_size_labels((uint8_t*)"\003org", &sz) == 2);
|
||||
unit_assert(sz == 5);
|
||||
unit_assert(dname_count_size_labels((uint8_t*)"\007example\003com",
|
||||
&sz) == 3);
|
||||
unit_assert(sz == 13);
|
||||
unit_assert(dname_count_size_labels((uint8_t*)"\003bla\007example"
|
||||
"\003com", &sz) == 4);
|
||||
unit_assert(sz == 17);
|
||||
}
|
||||
|
||||
|
||||
/** test pkt_dname_len */
|
||||
static void
|
||||
dname_test_pkt_dname_len(sldns_buffer* buff)
|
||||
{
|
||||
unit_show_func("util/data/dname.c", "pkt_dname_len");
|
||||
sldns_buffer_clear(buff);
|
||||
sldns_buffer_write(buff, "\000", 1);
|
||||
sldns_buffer_flip(buff);
|
||||
unit_assert( pkt_dname_len(buff) == 1 );
|
||||
unit_assert( sldns_buffer_position(buff) == 1);
|
||||
|
||||
sldns_buffer_clear(buff);
|
||||
sldns_buffer_write(buff, "\003org\000", 5);
|
||||
sldns_buffer_flip(buff);
|
||||
unit_assert( pkt_dname_len(buff) == 5 );
|
||||
unit_assert( sldns_buffer_position(buff) == 5);
|
||||
|
||||
sldns_buffer_clear(buff);
|
||||
sldns_buffer_write(buff, "\002os\007example\003org\000", 16);
|
||||
sldns_buffer_flip(buff);
|
||||
unit_assert( pkt_dname_len(buff) == 16 );
|
||||
unit_assert( sldns_buffer_position(buff) == 16);
|
||||
|
||||
/* invalid compression pointer: to self */
|
||||
sldns_buffer_clear(buff);
|
||||
sldns_buffer_write(buff, "\300\000os\007example\003org\000", 17);
|
||||
sldns_buffer_flip(buff);
|
||||
unit_assert( pkt_dname_len(buff) == 0 );
|
||||
|
||||
/* valid compression pointer */
|
||||
sldns_buffer_clear(buff);
|
||||
sldns_buffer_write(buff, "\003com\000\040\300\000", 8);
|
||||
sldns_buffer_flip(buff);
|
||||
sldns_buffer_set_position(buff, 6);
|
||||
unit_assert( pkt_dname_len(buff) == 5 );
|
||||
unit_assert( sldns_buffer_position(buff) == 8);
|
||||
|
||||
/* unknown label type */
|
||||
sldns_buffer_clear(buff);
|
||||
sldns_buffer_write(buff, "\002os\107example\003org\000", 16);
|
||||
sldns_buffer_flip(buff);
|
||||
unit_assert( pkt_dname_len(buff) == 0 );
|
||||
|
||||
/* label too long */
|
||||
sldns_buffer_clear(buff);
|
||||
sldns_buffer_write(buff, "\002os\047example\003org\000", 16);
|
||||
sldns_buffer_flip(buff);
|
||||
unit_assert( pkt_dname_len(buff) == 0 );
|
||||
|
||||
/* label exceeds packet */
|
||||
sldns_buffer_clear(buff);
|
||||
sldns_buffer_write(buff, "\002os\007example\007org\004", 16);
|
||||
sldns_buffer_flip(buff);
|
||||
unit_assert( pkt_dname_len(buff) == 0 );
|
||||
|
||||
/* name very long */
|
||||
sldns_buffer_clear(buff);
|
||||
sldns_buffer_write(buff,
|
||||
"\020a1cdef5555544444"
|
||||
"\020a2cdef5555544444"
|
||||
"\020a3cdef5555544444"
|
||||
"\020a4cdef5555544444"
|
||||
"\020a5cdef5555544444"
|
||||
"\020a6cdef5555544444"
|
||||
"\020a7cdef5555544444"
|
||||
"\020a8cdef5555544444"
|
||||
"\020a9cdef5555544444"
|
||||
"\020aAcdef5555544444"
|
||||
"\020aBcdef5555544444"
|
||||
"\020aCcdef5555544444"
|
||||
"\020aDcdef5555544444"
|
||||
"\020aEcdef5555544444" /* 238 up to here */
|
||||
"\007aabbccd" /* 246 up to here */
|
||||
"\007example\000" /* 255 to here */
|
||||
, 255);
|
||||
sldns_buffer_flip(buff);
|
||||
unit_assert( pkt_dname_len(buff) == 255 );
|
||||
unit_assert( sldns_buffer_position(buff) == 255);
|
||||
|
||||
/* name too long */
|
||||
sldns_buffer_clear(buff);
|
||||
sldns_buffer_write(buff,
|
||||
"\020a1cdef5555544444"
|
||||
"\020a2cdef5555544444"
|
||||
"\020a3cdef5555544444"
|
||||
"\020a4cdef5555544444"
|
||||
"\020a5cdef5555544444"
|
||||
"\020a6cdef5555544444"
|
||||
"\020a7cdef5555544444"
|
||||
"\020a8cdef5555544444"
|
||||
"\020a9cdef5555544444"
|
||||
"\020aAcdef5555544444"
|
||||
"\020aBcdef5555544444"
|
||||
"\020aCcdef5555544444"
|
||||
"\020aXcdef5555544444"
|
||||
"\020aXcdef5555544444"
|
||||
"\020aXcdef5555544444"
|
||||
"\020aDcdef5555544444"
|
||||
"\020aEcdef5555544444" /* 238 up to here */
|
||||
"\007aabbccd" /* 246 up to here */
|
||||
"\007example\000" /* 255 to here */
|
||||
, 255);
|
||||
sldns_buffer_flip(buff);
|
||||
unit_assert( pkt_dname_len(buff) == 0 );
|
||||
}
|
||||
|
||||
/** test dname_lab_cmp */
|
||||
static void
|
||||
dname_test_dname_lab_cmp(void)
|
||||
{
|
||||
int ml = 0; /* number of labels that matched exactly */
|
||||
unit_show_func("util/data/dname.c", "dname_lab_cmp");
|
||||
|
||||
/* test for equality succeeds */
|
||||
unit_assert(dname_lab_cmp((uint8_t*)"", 1, (uint8_t*)"", 1, &ml) == 0);
|
||||
unit_assert(ml == 1);
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"\003net", 2,
|
||||
(uint8_t*)"\003net", 2,
|
||||
&ml) == 0);
|
||||
unit_assert(ml == 2);
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"\007example\003net", 3,
|
||||
(uint8_t*)"\007example\003net", 3,
|
||||
&ml) == 0);
|
||||
unit_assert(ml == 3);
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"\004test\007example\003net", 4,
|
||||
(uint8_t*)"\004test\007example\003net", 4,
|
||||
&ml) == 0);
|
||||
unit_assert(ml == 4);
|
||||
|
||||
/* root is smaller than anything else */
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"", 1,
|
||||
(uint8_t*)"\003net", 2,
|
||||
&ml) == -1);
|
||||
unit_assert(ml == 1);
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"\003net", 2,
|
||||
(uint8_t*)"", 1,
|
||||
&ml) == 1);
|
||||
unit_assert(ml == 1);
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"", 1,
|
||||
(uint8_t*)"\007example\003net", 3,
|
||||
&ml) == -1);
|
||||
unit_assert(ml == 1);
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"\007example\003net", 3,
|
||||
(uint8_t*)"", 1,
|
||||
&ml) == 1);
|
||||
unit_assert(ml == 1);
|
||||
|
||||
/* label length makes a difference */
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"\004neta", 2,
|
||||
(uint8_t*)"\003net", 2,
|
||||
&ml) != 0);
|
||||
unit_assert(ml == 1);
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"\002ne", 2,
|
||||
(uint8_t*)"\004neta", 2,
|
||||
&ml) != 0);
|
||||
unit_assert(ml == 1);
|
||||
|
||||
/* contents follow the zone apex */
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"\003bla\007example\003net", 4,
|
||||
(uint8_t*)"\007example\003net", 3,
|
||||
&ml) == 1);
|
||||
unit_assert(ml == 3);
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"\007example\003net", 3,
|
||||
(uint8_t*)"\003bla\007example\003net", 4,
|
||||
&ml) == -1);
|
||||
unit_assert(ml == 3);
|
||||
|
||||
/* label content makes a difference */
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"\003aag\007example\003net", 4,
|
||||
(uint8_t*)"\003bla\007example\003net", 4,
|
||||
&ml) == -1);
|
||||
unit_assert(ml == 3);
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"\003aag\007example\003net", 4,
|
||||
(uint8_t*)"\003bla\007example\003net", 4,
|
||||
&ml) == -1);
|
||||
unit_assert(ml == 3);
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"\003bla\003aag\007example\003net", 5,
|
||||
(uint8_t*)"\003aag\003bla\007example\003net", 5,
|
||||
&ml) == -1);
|
||||
unit_assert(ml == 3);
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"\02sn\003opt\003aag\007example\003net", 6,
|
||||
(uint8_t*)"\02sn\003opt\003bla\007example\003net", 6,
|
||||
&ml) == -1);
|
||||
unit_assert(ml == 3);
|
||||
|
||||
/* but lowercase/uppercase does not make a difference. */
|
||||
unit_assert(dname_lab_cmp(
|
||||
(uint8_t*)"\003bLa\007examPLe\003net", 4,
|
||||
(uint8_t*)"\003bla\007eXAmple\003nET", 4,
|
||||
&ml) == 0);
|
||||
unit_assert(ml == 4);
|
||||
}
|
||||
|
||||
/** test dname_subdomain_c */
|
||||
static void
|
||||
dname_test_subdomain(void)
|
||||
{
|
||||
unit_show_func("util/data/dname.c", "dname_subdomain");
|
||||
unit_assert(dname_subdomain_c(
|
||||
(uint8_t*)"",
|
||||
(uint8_t*)""));
|
||||
unit_assert(dname_subdomain_c(
|
||||
(uint8_t*)"\003com",
|
||||
(uint8_t*)""));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"",
|
||||
(uint8_t*)"\003com"));
|
||||
unit_assert(dname_subdomain_c(
|
||||
(uint8_t*)"\007example\003com",
|
||||
(uint8_t*)"\003com"));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"\003com",
|
||||
(uint8_t*)"\007example\003com"));
|
||||
unit_assert(dname_subdomain_c(
|
||||
(uint8_t*)"\007example\003com",
|
||||
(uint8_t*)""));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"\003net",
|
||||
(uint8_t*)"\003com"));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"\003net",
|
||||
(uint8_t*)"\003org"));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"\007example\003net",
|
||||
(uint8_t*)"\003org"));
|
||||
unit_assert(!dname_subdomain_c(
|
||||
(uint8_t*)"\003net",
|
||||
(uint8_t*)"\007example\003org"));
|
||||
}
|
||||
|
||||
/** test dname_strict_subdomain */
|
||||
static void
|
||||
dname_test_strict_subdomain(void)
|
||||
{
|
||||
unit_show_func("util/data/dname.c", "dname_strict_subdomain");
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"", 1,
|
||||
(uint8_t*)"", 1));
|
||||
unit_assert(dname_strict_subdomain(
|
||||
(uint8_t*)"\003com", 2,
|
||||
(uint8_t*)"", 1));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"", 1,
|
||||
(uint8_t*)"\003com", 2));
|
||||
unit_assert(dname_strict_subdomain(
|
||||
(uint8_t*)"\007example\003com", 3,
|
||||
(uint8_t*)"\003com", 2));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"\003com", 2,
|
||||
(uint8_t*)"\007example\003com", 3));
|
||||
unit_assert(dname_strict_subdomain(
|
||||
(uint8_t*)"\007example\003com", 3,
|
||||
(uint8_t*)"", 1));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"\003net", 2,
|
||||
(uint8_t*)"\003com", 2));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"\003net", 2,
|
||||
(uint8_t*)"\003org", 2));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"\007example\003net", 3,
|
||||
(uint8_t*)"\003org", 2));
|
||||
unit_assert(!dname_strict_subdomain(
|
||||
(uint8_t*)"\003net", 2,
|
||||
(uint8_t*)"\007example\003org", 3));
|
||||
}
|
||||
|
||||
/** test dname_is_root */
|
||||
static void
|
||||
dname_test_isroot(void)
|
||||
{
|
||||
unit_show_func("util/data/dname.c", "dname_isroot");
|
||||
unit_assert(dname_is_root((uint8_t*)"\000"));
|
||||
unit_assert(!dname_is_root((uint8_t*)"\001a\000"));
|
||||
unit_assert(!dname_is_root((uint8_t*)"\005abvcd\003com\000"));
|
||||
/* malformed dname in this test, but should work */
|
||||
unit_assert(!dname_is_root((uint8_t*)"\077a\000"));
|
||||
unit_assert(dname_is_root((uint8_t*)"\000"));
|
||||
}
|
||||
|
||||
/** test dname_remove_label */
|
||||
static void
|
||||
dname_test_removelabel(void)
|
||||
{
|
||||
uint8_t* orig = (uint8_t*)"\007example\003com\000";
|
||||
uint8_t* n = orig;
|
||||
size_t l = 13;
|
||||
unit_show_func("util/data/dname.c", "dname_remove_label");
|
||||
dname_remove_label(&n, &l);
|
||||
unit_assert( n == orig+8 );
|
||||
unit_assert( l == 5 );
|
||||
dname_remove_label(&n, &l);
|
||||
unit_assert( n == orig+12 );
|
||||
unit_assert( l == 1 );
|
||||
dname_remove_label(&n, &l);
|
||||
unit_assert( n == orig+12 );
|
||||
unit_assert( l == 1 );
|
||||
}
|
||||
|
||||
/** test dname_signame_label_count */
|
||||
static void
|
||||
dname_test_sigcount(void)
|
||||
{
|
||||
unit_show_func("util/data/dname.c", "dname_signame_label_count");
|
||||
unit_assert(dname_signame_label_count((uint8_t*)"\000") == 0);
|
||||
unit_assert(dname_signame_label_count((uint8_t*)"\001*\000") == 0);
|
||||
unit_assert(dname_signame_label_count((uint8_t*)"\003xom\000") == 1);
|
||||
unit_assert(dname_signame_label_count(
|
||||
(uint8_t*)"\001*\003xom\000") == 1);
|
||||
unit_assert(dname_signame_label_count(
|
||||
(uint8_t*)"\007example\003xom\000") == 2);
|
||||
unit_assert(dname_signame_label_count(
|
||||
(uint8_t*)"\001*\007example\003xom\000") == 2);
|
||||
unit_assert(dname_signame_label_count(
|
||||
(uint8_t*)"\003www\007example\003xom\000") == 3);
|
||||
unit_assert(dname_signame_label_count(
|
||||
(uint8_t*)"\001*\003www\007example\003xom\000") == 3);
|
||||
}
|
||||
|
||||
/** test dname_is_wild routine */
|
||||
static void
|
||||
dname_test_iswild(void)
|
||||
{
|
||||
unit_show_func("util/data/dname.c", "dname_iswild");
|
||||
unit_assert( !dname_is_wild((uint8_t*)"\000") );
|
||||
unit_assert( dname_is_wild((uint8_t*)"\001*\000") );
|
||||
unit_assert( !dname_is_wild((uint8_t*)"\003net\000") );
|
||||
unit_assert( dname_is_wild((uint8_t*)"\001*\003net\000") );
|
||||
}
|
||||
|
||||
/** test dname_canonical_compare */
|
||||
static void
|
||||
dname_test_canoncmp(void)
|
||||
{
|
||||
unit_show_func("util/data/dname.c", "dname_canonical_compare");
|
||||
/* equality */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\000",
|
||||
(uint8_t*)"\000"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003net\000",
|
||||
(uint8_t*)"\003net\000"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example\003net\000",
|
||||
(uint8_t*)"\007example\003net\000"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\004test\007example\003net\000",
|
||||
(uint8_t*)"\004test\007example\003net\000"
|
||||
) == 0);
|
||||
|
||||
/* subdomains */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003com",
|
||||
(uint8_t*)"\000"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\000",
|
||||
(uint8_t*)"\003com"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example\003com",
|
||||
(uint8_t*)"\003com"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003com",
|
||||
(uint8_t*)"\007example\003com"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example\003com",
|
||||
(uint8_t*)"\000"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\000",
|
||||
(uint8_t*)"\007example\003com"
|
||||
) == -1);
|
||||
|
||||
/* compare rightmost label */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003com",
|
||||
(uint8_t*)"\003net"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003net",
|
||||
(uint8_t*)"\003com"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003net",
|
||||
(uint8_t*)"\003org"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example\003net",
|
||||
(uint8_t*)"\003org"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003org",
|
||||
(uint8_t*)"\007example\003net"
|
||||
) == 1);
|
||||
|
||||
/* label length makes a difference; but only if rest is equal */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\004neta",
|
||||
(uint8_t*)"\003net"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\002ne",
|
||||
(uint8_t*)"\004neta"
|
||||
) == -1);
|
||||
|
||||
/* label content */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003aag\007example\003net",
|
||||
(uint8_t*)"\003bla\007example\003net"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003bla\007example\003net",
|
||||
(uint8_t*)"\003aag\007example\003net"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003bla\003aag\007example\003net",
|
||||
(uint8_t*)"\003aag\003bla\007example\003net"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\02sn\003opt\003aag\007example\003net",
|
||||
(uint8_t*)"\02sn\003opt\003bla\007example\003net"
|
||||
) == -1);
|
||||
|
||||
/* lowercase during compare */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003bLa\007examPLe\003net",
|
||||
(uint8_t*)"\003bla\007eXAmple\003nET"
|
||||
) == 0);
|
||||
|
||||
/* example from 4034 */
|
||||
/* example a.example yljkjljk.a.example Z.a.example zABC.a.EXAMPLE
|
||||
z.example \001.z.example *.z.example \200.z.example */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"",
|
||||
(uint8_t*)"\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example",
|
||||
(uint8_t*)"\001a\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001a\007example",
|
||||
(uint8_t*)"\010yljkjljk\001a\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\010yljkjljk\001a\007example",
|
||||
(uint8_t*)"\001Z\001a\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001Z\001a\007example",
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE",
|
||||
(uint8_t*)"\001z\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001z\007example",
|
||||
(uint8_t*)"\001\001\001z\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001\001\001z\007example",
|
||||
(uint8_t*)"\001*\001z\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001*\001z\007example",
|
||||
(uint8_t*)"\001\200\001z\007example"
|
||||
) == -1);
|
||||
/* same example in reverse */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example",
|
||||
(uint8_t*)""
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001a\007example",
|
||||
(uint8_t*)"\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\010yljkjljk\001a\007example",
|
||||
(uint8_t*)"\001a\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001Z\001a\007example",
|
||||
(uint8_t*)"\010yljkjljk\001a\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE",
|
||||
(uint8_t*)"\001Z\001a\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001z\007example",
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001\001\001z\007example",
|
||||
(uint8_t*)"\001z\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001*\001z\007example",
|
||||
(uint8_t*)"\001\001\001z\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001\200\001z\007example",
|
||||
(uint8_t*)"\001*\001z\007example"
|
||||
) == 1);
|
||||
/* same example for equality */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example",
|
||||
(uint8_t*)"\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001a\007example",
|
||||
(uint8_t*)"\001a\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\010yljkjljk\001a\007example",
|
||||
(uint8_t*)"\010yljkjljk\001a\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001Z\001a\007example",
|
||||
(uint8_t*)"\001Z\001a\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE",
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001z\007example",
|
||||
(uint8_t*)"\001z\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001\001\001z\007example",
|
||||
(uint8_t*)"\001\001\001z\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001*\001z\007example",
|
||||
(uint8_t*)"\001*\001z\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001\200\001z\007example",
|
||||
(uint8_t*)"\001\200\001z\007example"
|
||||
) == 0);
|
||||
}
|
||||
|
||||
/** Test dname_get_shared_topdomain */
|
||||
static void
|
||||
dname_test_topdomain(void)
|
||||
{
|
||||
unit_show_func("util/data/dname.c", "dname_get_shared_topdomain");
|
||||
unit_assert( query_dname_compare(
|
||||
dname_get_shared_topdomain(
|
||||
(uint8_t*)"",
|
||||
(uint8_t*)""),
|
||||
(uint8_t*)"") == 0);
|
||||
unit_assert( query_dname_compare(
|
||||
dname_get_shared_topdomain(
|
||||
(uint8_t*)"\003www\007example\003com",
|
||||
(uint8_t*)"\003www\007example\003com"),
|
||||
(uint8_t*)"\003www\007example\003com") == 0);
|
||||
unit_assert( query_dname_compare(
|
||||
dname_get_shared_topdomain(
|
||||
(uint8_t*)"\003www\007example\003com",
|
||||
(uint8_t*)"\003bla\007example\003com"),
|
||||
(uint8_t*)"\007example\003com") == 0);
|
||||
}
|
||||
|
||||
/** Test dname_valid */
|
||||
static void
|
||||
dname_test_valid(void)
|
||||
{
|
||||
unit_show_func("util/data/dname.c", "dname_valid");
|
||||
unit_assert( dname_valid(
|
||||
(uint8_t*)"\003www\007example\003com", 255) == 17);
|
||||
unit_assert( dname_valid((uint8_t*)"", 255) == 1);
|
||||
unit_assert( dname_valid( (uint8_t*)
|
||||
"\020a1cdef5555544444"
|
||||
"\020a2cdef5555544444"
|
||||
"\020a3cdef5555544444"
|
||||
"\020a4cdef5555544444"
|
||||
"\020a5cdef5555544444"
|
||||
"\020a6cdef5555544444"
|
||||
"\020a7cdef5555544444"
|
||||
"\020a8cdef5555544444"
|
||||
"\020a9cdef5555544444"
|
||||
"\020aAcdef5555544444"
|
||||
"\020aBcdef5555544444"
|
||||
"\020aCcdef5555544444"
|
||||
"\020aDcdef5555544444"
|
||||
"\020aEcdef5555544444" /* 238 up to here */
|
||||
"\007aabbccd" /* 246 up to here */
|
||||
"\007example\000" /* 255 to here */
|
||||
, 255) == 255);
|
||||
unit_assert( dname_valid( (uint8_t*)
|
||||
"\020a1cdef5555544444"
|
||||
"\020a2cdef5555544444"
|
||||
"\020a3cdef5555544444"
|
||||
"\020a4cdef5555544444"
|
||||
"\020a5cdef5555544444"
|
||||
"\020a6cdef5555544444"
|
||||
"\020a7cdef5555544444"
|
||||
"\020a8cdef5555544444"
|
||||
"\020a9cdef5555544444"
|
||||
"\020aAcdef5555544444"
|
||||
"\020aBcdef5555544444"
|
||||
"\020aCcdef5555544444"
|
||||
"\020aDcdef5555544444"
|
||||
"\020aEcdef5555544444" /* 238 up to here */
|
||||
"\007aabbccd" /* 246 up to here */
|
||||
"\010exampleX\000" /* 256 to here */
|
||||
, 4096) == 0);
|
||||
}
|
||||
|
||||
/** test pkt_dname_tolower */
|
||||
static void
|
||||
dname_test_pdtl(sldns_buffer* loopbuf, sldns_buffer* boundbuf)
|
||||
{
|
||||
unit_show_func("util/data/dname.c", "pkt_dname_tolower");
|
||||
pkt_dname_tolower(loopbuf, sldns_buffer_at(loopbuf, 12));
|
||||
pkt_dname_tolower(boundbuf, sldns_buffer_at(boundbuf, 12));
|
||||
}
|
||||
|
||||
/** setup looped dname and out-of-bounds dname ptr */
|
||||
static void
|
||||
dname_setup_bufs(sldns_buffer* loopbuf, sldns_buffer* boundbuf)
|
||||
{
|
||||
sldns_buffer_write_u16(loopbuf, 0xd54d); /* id */
|
||||
sldns_buffer_write_u16(loopbuf, 0x12); /* flags */
|
||||
sldns_buffer_write_u16(loopbuf, 1); /* qdcount */
|
||||
sldns_buffer_write_u16(loopbuf, 0); /* ancount */
|
||||
sldns_buffer_write_u16(loopbuf, 0); /* nscount */
|
||||
sldns_buffer_write_u16(loopbuf, 0); /* arcount */
|
||||
sldns_buffer_write_u8(loopbuf, 0xc0); /* PTR back at itself */
|
||||
sldns_buffer_write_u8(loopbuf, 0x0c);
|
||||
sldns_buffer_flip(loopbuf);
|
||||
|
||||
sldns_buffer_write_u16(boundbuf, 0xd54d); /* id */
|
||||
sldns_buffer_write_u16(boundbuf, 0x12); /* flags */
|
||||
sldns_buffer_write_u16(boundbuf, 1); /* qdcount */
|
||||
sldns_buffer_write_u16(boundbuf, 0); /* ancount */
|
||||
sldns_buffer_write_u16(boundbuf, 0); /* nscount */
|
||||
sldns_buffer_write_u16(boundbuf, 0); /* arcount */
|
||||
sldns_buffer_write_u8(boundbuf, 0x01); /* len=1 */
|
||||
sldns_buffer_write_u8(boundbuf, (uint8_t)'A'); /* A. label */
|
||||
sldns_buffer_write_u8(boundbuf, 0xc0); /* PTR out of bounds */
|
||||
sldns_buffer_write_u8(boundbuf, 0xcc);
|
||||
sldns_buffer_flip(boundbuf);
|
||||
}
|
||||
|
||||
void dname_test(void)
|
||||
{
|
||||
sldns_buffer* loopbuf = sldns_buffer_new(14);
|
||||
sldns_buffer* boundbuf = sldns_buffer_new(16);
|
||||
sldns_buffer* buff = sldns_buffer_new(65800);
|
||||
unit_assert(loopbuf && boundbuf && buff);
|
||||
sldns_buffer_flip(buff);
|
||||
dname_setup_bufs(loopbuf, boundbuf);
|
||||
dname_test_qdl(buff);
|
||||
dname_test_qdtl(buff);
|
||||
dname_test_pdtl(loopbuf, boundbuf);
|
||||
dname_test_query_dname_compare();
|
||||
dname_test_count_labels();
|
||||
dname_test_count_size_labels();
|
||||
dname_test_dname_lab_cmp();
|
||||
dname_test_pkt_dname_len(buff);
|
||||
dname_test_strict_subdomain();
|
||||
dname_test_subdomain();
|
||||
dname_test_isroot();
|
||||
dname_test_removelabel();
|
||||
dname_test_sigcount();
|
||||
dname_test_iswild();
|
||||
dname_test_canoncmp();
|
||||
dname_test_topdomain();
|
||||
dname_test_valid();
|
||||
sldns_buffer_free(buff);
|
||||
sldns_buffer_free(loopbuf);
|
||||
sldns_buffer_free(boundbuf);
|
||||
}
|
||||
218
external/unbound/testcode/unitldns.c
vendored
Normal file
218
external/unbound/testcode/unitldns.c
vendored
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* testcode/unitldns.c - unit test for ldns routines.
|
||||
*
|
||||
* Copyright (c) 2014, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* \file
|
||||
* Calls ldns unit tests. Exits with code 1 on a failure.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "testcode/unitmain.h"
|
||||
#include "ldns/sbuffer.h"
|
||||
#include "ldns/str2wire.h"
|
||||
#include "ldns/wire2str.h"
|
||||
|
||||
/** verbose this unit test */
|
||||
static int vbmp = 0;
|
||||
|
||||
/** print buffer to hex into string */
|
||||
static void
|
||||
buf_to_hex(uint8_t* b, size_t blen, char* s, size_t slen)
|
||||
{
|
||||
const char* h = "0123456789ABCDEF";
|
||||
size_t i;
|
||||
if(slen < blen*2+2 && vbmp) printf("hexstring buffer too small\n");
|
||||
unit_assert(slen >= blen*2+2);
|
||||
for(i=0; i<blen; i++) {
|
||||
s[i*2] = h[(b[i]&0xf0)>>4];
|
||||
s[i*2+1] = h[b[i]&0x0f];
|
||||
}
|
||||
s[blen*2] = '\n';
|
||||
s[blen*2+1] = 0;
|
||||
}
|
||||
|
||||
/** Transform input.
|
||||
* @param txt_in: input text format.
|
||||
* @param wire1: output wireformat in hex (txt_in converted to wire).
|
||||
* @param txt_out: output text format (converted from wire_out).
|
||||
* @param wire2: output wireformat in hex, txt_out converted back to wireformat.
|
||||
* @param bufs: size of the text buffers.
|
||||
*/
|
||||
static void
|
||||
rr_transform(char* txt_in, char* wire1, char* txt_out, char* wire2,
|
||||
size_t bufs)
|
||||
{
|
||||
uint8_t b[65536];
|
||||
size_t len;
|
||||
int err;
|
||||
|
||||
len = sizeof(b);
|
||||
err = sldns_str2wire_rr_buf(txt_in, b, &len, NULL, 3600,
|
||||
NULL, 0, NULL, 0);
|
||||
if(err != 0) {
|
||||
if(vbmp) printf("sldns_str2wire_rr_buf, pos %d: %s\n",
|
||||
LDNS_WIREPARSE_OFFSET(err),
|
||||
sldns_get_errorstr_parse(err));
|
||||
}
|
||||
unit_assert(err == 0);
|
||||
buf_to_hex(b, len, wire1, bufs);
|
||||
if(vbmp) printf("wire1: %s", wire1);
|
||||
|
||||
err = sldns_wire2str_rr_buf(b, len, txt_out, bufs);
|
||||
unit_assert(err < (int)bufs && err > 0);
|
||||
if(vbmp) printf("txt: %s", txt_out);
|
||||
|
||||
len = sizeof(b);
|
||||
err = sldns_str2wire_rr_buf(txt_out, b, &len, NULL, 3600,
|
||||
NULL, 0, NULL, 0);
|
||||
if(err != 0) {
|
||||
if(vbmp) printf("sldns_str2wire_rr_buf-2, pos %d: %s\n",
|
||||
LDNS_WIREPARSE_OFFSET(err),
|
||||
sldns_get_errorstr_parse(err));
|
||||
}
|
||||
unit_assert(err == 0);
|
||||
buf_to_hex(b, len, wire2, bufs);
|
||||
if(vbmp) printf("wire2: %s", wire2);
|
||||
}
|
||||
|
||||
/** Check if results are correct */
|
||||
static void
|
||||
rr_checks(char* wire_chk, char* txt_chk, char* txt_out, char* wire_out,
|
||||
char* back)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
/* the wiretostr on ipv6 is weird on apple, we cannot check it.
|
||||
* skip AAAA on OSX */
|
||||
if(strstr(txt_out, "IN AAAA"))
|
||||
txt_out = txt_chk; /* skip this test, but test wirefmt */
|
||||
/* so we know that txt_out back to wire is the same */
|
||||
#endif
|
||||
|
||||
if(strcmp(txt_chk, txt_out) != 0 && vbmp)
|
||||
printf("txt different\n");
|
||||
if(strcmp(wire_chk, wire_out) != 0 && vbmp)
|
||||
printf("wire1 different\n");
|
||||
if(strcmp(wire_chk, back) != 0 && vbmp)
|
||||
printf("wire2 different\n");
|
||||
|
||||
unit_assert(strcmp(txt_chk, txt_out) == 0);
|
||||
unit_assert(strcmp(wire_chk, wire_out) == 0);
|
||||
unit_assert(strcmp(wire_chk, back) == 0);
|
||||
}
|
||||
|
||||
/** read rrs to and from string, and wireformat
|
||||
* Skips empty lines and comments.
|
||||
* @param input: input file with text format.
|
||||
* @param check: check file with hex and then textformat
|
||||
*/
|
||||
static void
|
||||
rr_test_file(const char* input, const char* check)
|
||||
{
|
||||
size_t bufs = 131072;
|
||||
FILE* inf, *chf, *of;
|
||||
int lineno = 0, chlineno = 0;
|
||||
char* txt_in = (char*)malloc(bufs);
|
||||
char* txt_out = (char*)malloc(bufs);
|
||||
char* txt_chk = (char*)malloc(bufs);
|
||||
char* wire_out = (char*)malloc(bufs);
|
||||
char* wire_chk = (char*)malloc(bufs);
|
||||
char* back = (char*)malloc(bufs);
|
||||
if(!txt_in || !txt_out || !txt_chk || !wire_out || !wire_chk || !back)
|
||||
fatal_exit("malloc failure");
|
||||
inf = fopen(input, "r");
|
||||
if(!inf) fatal_exit("cannot open %s: %s", input, strerror(errno));
|
||||
chf = fopen(check, "r");
|
||||
if(!chf) fatal_exit("cannot open %s: %s", check, strerror(errno));
|
||||
|
||||
of = NULL;
|
||||
if(0) {
|
||||
/* debug: create check file */
|
||||
of = fopen("outputfile", "w");
|
||||
if(!of) fatal_exit("cannot write output: %s", strerror(errno));
|
||||
}
|
||||
|
||||
while(fgets(txt_in, (int)bufs, inf)) {
|
||||
lineno++;
|
||||
if(vbmp) printf("\n%s:%d %s", input, lineno, txt_in);
|
||||
/* skip empty lines and comments */
|
||||
if(txt_in[0] == 0 || txt_in[0] == '\n' || txt_in[0] == ';')
|
||||
continue;
|
||||
/* read check lines */
|
||||
if(!fgets(wire_chk, (int)bufs, chf))
|
||||
printf("%s too short\n", check);
|
||||
if(!fgets(txt_chk, (int)bufs, chf))
|
||||
printf("%s too short\n", check);
|
||||
chlineno += 2;
|
||||
if(vbmp) printf("%s:%d %s", check, chlineno-1, wire_chk);
|
||||
if(vbmp) printf("%s:%d %s", check, chlineno, txt_chk);
|
||||
/* generate results */
|
||||
rr_transform(txt_in, wire_out, txt_out, back, bufs);
|
||||
/* checks */
|
||||
if(of) {
|
||||
fprintf(of, "%s%s", wire_out, txt_out);
|
||||
} else {
|
||||
rr_checks(wire_chk, txt_chk, txt_out, wire_out, back);
|
||||
}
|
||||
}
|
||||
|
||||
if(of) fclose(of);
|
||||
fclose(inf);
|
||||
fclose(chf);
|
||||
free(txt_in);
|
||||
free(txt_out);
|
||||
free(txt_chk);
|
||||
free(wire_out);
|
||||
free(wire_chk);
|
||||
free(back);
|
||||
}
|
||||
|
||||
/** read rrs to and from string, to and from wireformat */
|
||||
static void
|
||||
rr_tests(void)
|
||||
{
|
||||
rr_test_file("testdata/test_ldnsrr.1", "testdata/test_ldnsrr.c1");
|
||||
rr_test_file("testdata/test_ldnsrr.2", "testdata/test_ldnsrr.c2");
|
||||
rr_test_file("testdata/test_ldnsrr.3", "testdata/test_ldnsrr.c3");
|
||||
rr_test_file("testdata/test_ldnsrr.4", "testdata/test_ldnsrr.c4");
|
||||
rr_test_file("testdata/test_ldnsrr.5", "testdata/test_ldnsrr.c5");
|
||||
}
|
||||
|
||||
void
|
||||
ldns_test(void)
|
||||
{
|
||||
unit_show_feature("sldns");
|
||||
rr_tests();
|
||||
}
|
||||
499
external/unbound/testcode/unitlruhash.c
vendored
Normal file
499
external/unbound/testcode/unitlruhash.c
vendored
Normal file
|
|
@ -0,0 +1,499 @@
|
|||
/*
|
||||
* testcode/unitlruhash.c - unit test for lruhash table.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* \file
|
||||
* Tests the locking LRU keeping hash table implementation.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "testcode/unitmain.h"
|
||||
#include "util/log.h"
|
||||
#include "util/storage/lruhash.h"
|
||||
#include "util/storage/slabhash.h" /* for the test structures */
|
||||
|
||||
/** use this type for the lruhash test key */
|
||||
typedef struct slabhash_testkey testkey_t;
|
||||
/** use this type for the lruhash test data */
|
||||
typedef struct slabhash_testdata testdata_t;
|
||||
|
||||
/** delete key */
|
||||
static void delkey(struct slabhash_testkey* k) {
|
||||
lock_rw_destroy(&k->entry.lock); free(k);}
|
||||
/** delete data */
|
||||
static void deldata(struct slabhash_testdata* d) {free(d);}
|
||||
|
||||
/** hash func, very bad to improve collisions */
|
||||
static hashvalue_t myhash(int id) {return (hashvalue_t)id & 0x0f;}
|
||||
/** allocate new key, fill in hash */
|
||||
static testkey_t* newkey(int id) {
|
||||
testkey_t* k = (testkey_t*)calloc(1, sizeof(testkey_t));
|
||||
if(!k) fatal_exit("out of memory");
|
||||
k->id = id;
|
||||
k->entry.hash = myhash(id);
|
||||
k->entry.key = k;
|
||||
lock_rw_init(&k->entry.lock);
|
||||
return k;
|
||||
}
|
||||
/** new data el */
|
||||
static testdata_t* newdata(int val) {
|
||||
testdata_t* d = (testdata_t*)calloc(1,
|
||||
sizeof(testdata_t));
|
||||
if(!d) fatal_exit("out of memory");
|
||||
d->data = val;
|
||||
return d;
|
||||
}
|
||||
|
||||
/** test bin_find_entry function and bin_overflow_remove */
|
||||
static void
|
||||
test_bin_find_entry(struct lruhash* table)
|
||||
{
|
||||
testkey_t* k = newkey(12);
|
||||
testdata_t* d = newdata(128);
|
||||
testkey_t* k2 = newkey(12 + 1024);
|
||||
testkey_t* k3 = newkey(14);
|
||||
testkey_t* k4 = newkey(12 + 1024*2);
|
||||
hashvalue_t h = myhash(12);
|
||||
struct lruhash_bin bin;
|
||||
memset(&bin, 0, sizeof(bin));
|
||||
bin_init(&bin, 1);
|
||||
|
||||
/* remove from empty list */
|
||||
bin_overflow_remove(&bin, &k->entry);
|
||||
|
||||
/* find in empty list */
|
||||
unit_assert( bin_find_entry(table, &bin, h, k) == NULL );
|
||||
|
||||
/* insert */
|
||||
lock_quick_lock(&bin.lock);
|
||||
bin.overflow_list = &k->entry;
|
||||
lock_quick_unlock(&bin.lock);
|
||||
|
||||
/* find, hash not OK. */
|
||||
unit_assert( bin_find_entry(table, &bin, myhash(13), k) == NULL );
|
||||
|
||||
/* find, hash OK, but cmp not */
|
||||
unit_assert( k->entry.hash == k2->entry.hash );
|
||||
unit_assert( bin_find_entry(table, &bin, h, k2) == NULL );
|
||||
|
||||
/* find, hash OK, and cmp too */
|
||||
unit_assert( bin_find_entry(table, &bin, h, k) == &k->entry );
|
||||
|
||||
/* remove the element */
|
||||
lock_quick_lock(&bin.lock);
|
||||
bin_overflow_remove(&bin, &k->entry);
|
||||
lock_quick_unlock(&bin.lock);
|
||||
unit_assert( bin_find_entry(table, &bin, h, k) == NULL );
|
||||
|
||||
/* prepend two different elements; so the list is long */
|
||||
/* one has the same hash, but different cmp */
|
||||
lock_quick_lock(&bin.lock);
|
||||
unit_assert( k->entry.hash == k4->entry.hash );
|
||||
k4->entry.overflow_next = &k->entry;
|
||||
k3->entry.overflow_next = &k4->entry;
|
||||
bin.overflow_list = &k3->entry;
|
||||
lock_quick_unlock(&bin.lock);
|
||||
|
||||
/* find, hash not OK. */
|
||||
unit_assert( bin_find_entry(table, &bin, myhash(13), k) == NULL );
|
||||
|
||||
/* find, hash OK, but cmp not */
|
||||
unit_assert( k->entry.hash == k2->entry.hash );
|
||||
unit_assert( bin_find_entry(table, &bin, h, k2) == NULL );
|
||||
|
||||
/* find, hash OK, and cmp too */
|
||||
unit_assert( bin_find_entry(table, &bin, h, k) == &k->entry );
|
||||
|
||||
/* remove middle element */
|
||||
unit_assert( bin_find_entry(table, &bin, k4->entry.hash, k4)
|
||||
== &k4->entry );
|
||||
lock_quick_lock(&bin.lock);
|
||||
bin_overflow_remove(&bin, &k4->entry);
|
||||
lock_quick_unlock(&bin.lock);
|
||||
unit_assert( bin_find_entry(table, &bin, k4->entry.hash, k4) == NULL);
|
||||
|
||||
/* remove last element */
|
||||
lock_quick_lock(&bin.lock);
|
||||
bin_overflow_remove(&bin, &k->entry);
|
||||
lock_quick_unlock(&bin.lock);
|
||||
unit_assert( bin_find_entry(table, &bin, h, k) == NULL );
|
||||
|
||||
lock_quick_destroy(&bin.lock);
|
||||
delkey(k);
|
||||
delkey(k2);
|
||||
delkey(k3);
|
||||
delkey(k4);
|
||||
deldata(d);
|
||||
}
|
||||
|
||||
/** test lru_front lru_remove */
|
||||
static void test_lru(struct lruhash* table)
|
||||
{
|
||||
testkey_t* k = newkey(12);
|
||||
testkey_t* k2 = newkey(14);
|
||||
lock_quick_lock(&table->lock);
|
||||
|
||||
unit_assert( table->lru_start == NULL && table->lru_end == NULL);
|
||||
lru_remove(table, &k->entry);
|
||||
unit_assert( table->lru_start == NULL && table->lru_end == NULL);
|
||||
|
||||
/* add one */
|
||||
lru_front(table, &k->entry);
|
||||
unit_assert( table->lru_start == &k->entry &&
|
||||
table->lru_end == &k->entry);
|
||||
/* remove it */
|
||||
lru_remove(table, &k->entry);
|
||||
unit_assert( table->lru_start == NULL && table->lru_end == NULL);
|
||||
|
||||
/* add two */
|
||||
lru_front(table, &k->entry);
|
||||
unit_assert( table->lru_start == &k->entry &&
|
||||
table->lru_end == &k->entry);
|
||||
lru_front(table, &k2->entry);
|
||||
unit_assert( table->lru_start == &k2->entry &&
|
||||
table->lru_end == &k->entry);
|
||||
/* remove first in list */
|
||||
lru_remove(table, &k2->entry);
|
||||
unit_assert( table->lru_start == &k->entry &&
|
||||
table->lru_end == &k->entry);
|
||||
lru_front(table, &k2->entry);
|
||||
unit_assert( table->lru_start == &k2->entry &&
|
||||
table->lru_end == &k->entry);
|
||||
/* remove last in list */
|
||||
lru_remove(table, &k->entry);
|
||||
unit_assert( table->lru_start == &k2->entry &&
|
||||
table->lru_end == &k2->entry);
|
||||
|
||||
/* empty the list */
|
||||
lru_remove(table, &k2->entry);
|
||||
unit_assert( table->lru_start == NULL && table->lru_end == NULL);
|
||||
lock_quick_unlock(&table->lock);
|
||||
delkey(k);
|
||||
delkey(k2);
|
||||
}
|
||||
|
||||
/** test hashtable using short sequence */
|
||||
static void
|
||||
test_short_table(struct lruhash* table)
|
||||
{
|
||||
testkey_t* k = newkey(12);
|
||||
testkey_t* k2 = newkey(14);
|
||||
testdata_t* d = newdata(128);
|
||||
testdata_t* d2 = newdata(129);
|
||||
|
||||
k->entry.data = d;
|
||||
k2->entry.data = d2;
|
||||
|
||||
lruhash_insert(table, myhash(12), &k->entry, d, NULL);
|
||||
lruhash_insert(table, myhash(14), &k2->entry, d2, NULL);
|
||||
|
||||
unit_assert( lruhash_lookup(table, myhash(12), k, 0) == &k->entry);
|
||||
lock_rw_unlock( &k->entry.lock );
|
||||
unit_assert( lruhash_lookup(table, myhash(14), k2, 0) == &k2->entry);
|
||||
lock_rw_unlock( &k2->entry.lock );
|
||||
lruhash_remove(table, myhash(12), k);
|
||||
lruhash_remove(table, myhash(14), k2);
|
||||
}
|
||||
|
||||
/** number of hash test max */
|
||||
#define HASHTESTMAX 25
|
||||
|
||||
/** test adding a random element */
|
||||
static void
|
||||
testadd(struct lruhash* table, testdata_t* ref[])
|
||||
{
|
||||
int numtoadd = random() % HASHTESTMAX;
|
||||
testdata_t* data = newdata(numtoadd);
|
||||
testkey_t* key = newkey(numtoadd);
|
||||
key->entry.data = data;
|
||||
lruhash_insert(table, myhash(numtoadd), &key->entry, data, NULL);
|
||||
ref[numtoadd] = data;
|
||||
}
|
||||
|
||||
/** test adding a random element */
|
||||
static void
|
||||
testremove(struct lruhash* table, testdata_t* ref[])
|
||||
{
|
||||
int num = random() % HASHTESTMAX;
|
||||
testkey_t* key = newkey(num);
|
||||
lruhash_remove(table, myhash(num), key);
|
||||
ref[num] = NULL;
|
||||
delkey(key);
|
||||
}
|
||||
|
||||
/** test adding a random element */
|
||||
static void
|
||||
testlookup(struct lruhash* table, testdata_t* ref[])
|
||||
{
|
||||
int num = random() % HASHTESTMAX;
|
||||
testkey_t* key = newkey(num);
|
||||
struct lruhash_entry* en = lruhash_lookup(table, myhash(num), key, 0);
|
||||
testdata_t* data = en? (testdata_t*)en->data : NULL;
|
||||
if(en) {
|
||||
unit_assert(en->key);
|
||||
unit_assert(en->data);
|
||||
}
|
||||
if(0) log_info("lookup %d got %d, expect %d", num, en? data->data :-1,
|
||||
ref[num]? ref[num]->data : -1);
|
||||
unit_assert( data == ref[num] );
|
||||
if(en) { lock_rw_unlock(&en->lock); }
|
||||
delkey(key);
|
||||
}
|
||||
|
||||
/** check integrity of hash table */
|
||||
static void
|
||||
check_table(struct lruhash* table)
|
||||
{
|
||||
struct lruhash_entry* p;
|
||||
size_t c = 0;
|
||||
lock_quick_lock(&table->lock);
|
||||
unit_assert( table->num <= table->size);
|
||||
unit_assert( table->size_mask == (int)table->size-1 );
|
||||
unit_assert( (table->lru_start && table->lru_end) ||
|
||||
(!table->lru_start && !table->lru_end) );
|
||||
unit_assert( table->space_used <= table->space_max );
|
||||
/* check lru list integrity */
|
||||
if(table->lru_start)
|
||||
unit_assert(table->lru_start->lru_prev == NULL);
|
||||
if(table->lru_end)
|
||||
unit_assert(table->lru_end->lru_next == NULL);
|
||||
p = table->lru_start;
|
||||
while(p) {
|
||||
if(p->lru_prev) {
|
||||
unit_assert(p->lru_prev->lru_next == p);
|
||||
}
|
||||
if(p->lru_next) {
|
||||
unit_assert(p->lru_next->lru_prev == p);
|
||||
}
|
||||
c++;
|
||||
p = p->lru_next;
|
||||
}
|
||||
unit_assert(c == table->num);
|
||||
|
||||
/* this assertion is specific to the unit test */
|
||||
unit_assert( table->space_used ==
|
||||
table->num * test_slabhash_sizefunc(NULL, NULL) );
|
||||
lock_quick_unlock(&table->lock);
|
||||
}
|
||||
|
||||
/** test adding a random element (unlimited range) */
|
||||
static void
|
||||
testadd_unlim(struct lruhash* table, testdata_t** ref)
|
||||
{
|
||||
int numtoadd = random() % (HASHTESTMAX * 10);
|
||||
testdata_t* data = newdata(numtoadd);
|
||||
testkey_t* key = newkey(numtoadd);
|
||||
key->entry.data = data;
|
||||
lruhash_insert(table, myhash(numtoadd), &key->entry, data, NULL);
|
||||
if(ref)
|
||||
ref[numtoadd] = data;
|
||||
}
|
||||
|
||||
/** test adding a random element (unlimited range) */
|
||||
static void
|
||||
testremove_unlim(struct lruhash* table, testdata_t** ref)
|
||||
{
|
||||
int num = random() % (HASHTESTMAX*10);
|
||||
testkey_t* key = newkey(num);
|
||||
lruhash_remove(table, myhash(num), key);
|
||||
if(ref)
|
||||
ref[num] = NULL;
|
||||
delkey(key);
|
||||
}
|
||||
|
||||
/** test adding a random element (unlimited range) */
|
||||
static void
|
||||
testlookup_unlim(struct lruhash* table, testdata_t** ref)
|
||||
{
|
||||
int num = random() % (HASHTESTMAX*10);
|
||||
testkey_t* key = newkey(num);
|
||||
struct lruhash_entry* en = lruhash_lookup(table, myhash(num), key, 0);
|
||||
testdata_t* data = en? (testdata_t*)en->data : NULL;
|
||||
if(en) {
|
||||
unit_assert(en->key);
|
||||
unit_assert(en->data);
|
||||
}
|
||||
if(0 && ref) log_info("lookup unlim %d got %d, expect %d", num, en ?
|
||||
data->data :-1, ref[num] ? ref[num]->data : -1);
|
||||
if(data && ref) {
|
||||
/* its okay for !data, it fell off the lru */
|
||||
unit_assert( data == ref[num] );
|
||||
}
|
||||
if(en) { lock_rw_unlock(&en->lock); }
|
||||
delkey(key);
|
||||
}
|
||||
|
||||
/** test with long sequence of adds, removes and updates, and lookups */
|
||||
static void
|
||||
test_long_table(struct lruhash* table)
|
||||
{
|
||||
/* assuming it all fits in the hastable, this check will work */
|
||||
testdata_t* ref[HASHTESTMAX * 100];
|
||||
size_t i;
|
||||
memset(ref, 0, sizeof(ref));
|
||||
/* test assumption */
|
||||
if(0) log_info(" size %d x %d < %d", (int)test_slabhash_sizefunc(NULL, NULL),
|
||||
(int)HASHTESTMAX, (int)table->space_max);
|
||||
unit_assert( test_slabhash_sizefunc(NULL, NULL)*HASHTESTMAX < table->space_max);
|
||||
if(0) lruhash_status(table, "unit test", 1);
|
||||
srandom(48);
|
||||
for(i=0; i<1000; i++) {
|
||||
/* what to do? */
|
||||
if(i == 500) {
|
||||
lruhash_clear(table);
|
||||
memset(ref, 0, sizeof(ref));
|
||||
continue;
|
||||
}
|
||||
switch(random() % 4) {
|
||||
case 0:
|
||||
case 3:
|
||||
testadd(table, ref);
|
||||
break;
|
||||
case 1:
|
||||
testremove(table, ref);
|
||||
break;
|
||||
case 2:
|
||||
testlookup(table, ref);
|
||||
break;
|
||||
default:
|
||||
unit_assert(0);
|
||||
}
|
||||
if(0) lruhash_status(table, "unit test", 1);
|
||||
check_table(table);
|
||||
unit_assert( table->num <= HASHTESTMAX );
|
||||
}
|
||||
|
||||
/* test more, but 'ref' assumption does not hold anymore */
|
||||
for(i=0; i<1000; i++) {
|
||||
/* what to do? */
|
||||
switch(random() % 4) {
|
||||
case 0:
|
||||
case 3:
|
||||
testadd_unlim(table, ref);
|
||||
break;
|
||||
case 1:
|
||||
testremove_unlim(table, ref);
|
||||
break;
|
||||
case 2:
|
||||
testlookup_unlim(table, ref);
|
||||
break;
|
||||
default:
|
||||
unit_assert(0);
|
||||
}
|
||||
if(0) lruhash_status(table, "unlim", 1);
|
||||
check_table(table);
|
||||
}
|
||||
}
|
||||
|
||||
/** structure to threaded test the lru hash table */
|
||||
struct test_thr {
|
||||
/** thread num, first entry. */
|
||||
int num;
|
||||
/** id */
|
||||
ub_thread_t id;
|
||||
/** hash table */
|
||||
struct lruhash* table;
|
||||
};
|
||||
|
||||
/** main routine for threaded hash table test */
|
||||
static void*
|
||||
test_thr_main(void* arg)
|
||||
{
|
||||
struct test_thr* t = (struct test_thr*)arg;
|
||||
int i;
|
||||
log_thread_set(&t->num);
|
||||
for(i=0; i<1000; i++) {
|
||||
switch(random() % 4) {
|
||||
case 0:
|
||||
case 3:
|
||||
testadd_unlim(t->table, NULL);
|
||||
break;
|
||||
case 1:
|
||||
testremove_unlim(t->table, NULL);
|
||||
break;
|
||||
case 2:
|
||||
testlookup_unlim(t->table, NULL);
|
||||
break;
|
||||
default:
|
||||
unit_assert(0);
|
||||
}
|
||||
if(0) lruhash_status(t->table, "hashtest", 1);
|
||||
if(i % 100 == 0) /* because of locking, not all the time */
|
||||
check_table(t->table);
|
||||
}
|
||||
check_table(t->table);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** test hash table access by multiple threads */
|
||||
static void
|
||||
test_threaded_table(struct lruhash* table)
|
||||
{
|
||||
int numth = 10;
|
||||
struct test_thr t[100];
|
||||
int i;
|
||||
|
||||
for(i=1; i<numth; i++) {
|
||||
t[i].num = i;
|
||||
t[i].table = table;
|
||||
ub_thread_create(&t[i].id, test_thr_main, &t[i]);
|
||||
}
|
||||
|
||||
for(i=1; i<numth; i++) {
|
||||
ub_thread_join(t[i].id);
|
||||
}
|
||||
if(0) lruhash_status(table, "hashtest", 1);
|
||||
}
|
||||
|
||||
void lruhash_test(void)
|
||||
{
|
||||
/* start very very small array, so it can do lots of table_grow() */
|
||||
/* also small in size so that reclaim has to be done quickly. */
|
||||
struct lruhash* table ;
|
||||
unit_show_feature("lruhash");
|
||||
table = lruhash_create(2, 8192,
|
||||
test_slabhash_sizefunc, test_slabhash_compfunc,
|
||||
test_slabhash_delkey, test_slabhash_deldata, NULL);
|
||||
test_bin_find_entry(table);
|
||||
test_lru(table);
|
||||
test_short_table(table);
|
||||
test_long_table(table);
|
||||
lruhash_delete(table);
|
||||
table = lruhash_create(2, 8192,
|
||||
test_slabhash_sizefunc, test_slabhash_compfunc,
|
||||
test_slabhash_delkey, test_slabhash_deldata, NULL);
|
||||
test_threaded_table(table);
|
||||
lruhash_delete(table);
|
||||
}
|
||||
617
external/unbound/testcode/unitmain.c
vendored
Normal file
617
external/unbound/testcode/unitmain.c
vendored
Normal file
|
|
@ -0,0 +1,617 @@
|
|||
/*
|
||||
* testcode/unitmain.c - unit test main program for unbound.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* \file
|
||||
* Unit test main program. Calls all the other unit tests.
|
||||
* Exits with code 1 on a failure. 0 if all unit tests are successfull.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#ifdef HAVE_OPENSSL_ERR_H
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENSSL_RAND_H
|
||||
#include <openssl/rand.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENSSL_CONF_H
|
||||
#include <openssl/conf.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENSSL_ENGINE_H
|
||||
#include <openssl/engine.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NSS
|
||||
/* nss3 */
|
||||
#include "nss.h"
|
||||
#endif
|
||||
|
||||
#include "ldns/rrdef.h"
|
||||
#include "ldns/keyraw.h"
|
||||
#include "util/log.h"
|
||||
#include "testcode/unitmain.h"
|
||||
|
||||
/** number of tests done */
|
||||
int testcount = 0;
|
||||
|
||||
#include "util/alloc.h"
|
||||
/** test alloc code */
|
||||
static void
|
||||
alloc_test(void) {
|
||||
alloc_special_t *t1, *t2;
|
||||
struct alloc_cache major, minor1, minor2;
|
||||
int i;
|
||||
|
||||
unit_show_feature("alloc_special_obtain");
|
||||
alloc_init(&major, NULL, 0);
|
||||
alloc_init(&minor1, &major, 0);
|
||||
alloc_init(&minor2, &major, 1);
|
||||
|
||||
t1 = alloc_special_obtain(&minor1);
|
||||
alloc_clear(&minor1);
|
||||
|
||||
alloc_special_release(&minor2, t1);
|
||||
t2 = alloc_special_obtain(&minor2);
|
||||
unit_assert( t1 == t2 ); /* reused */
|
||||
alloc_special_release(&minor2, t1);
|
||||
|
||||
for(i=0; i<100; i++) {
|
||||
t1 = alloc_special_obtain(&minor1);
|
||||
alloc_special_release(&minor2, t1);
|
||||
}
|
||||
if(0) {
|
||||
alloc_stats(&minor1);
|
||||
alloc_stats(&minor2);
|
||||
alloc_stats(&major);
|
||||
}
|
||||
/* reuse happened */
|
||||
unit_assert(minor1.num_quar + minor2.num_quar + major.num_quar == 11);
|
||||
|
||||
alloc_clear(&minor1);
|
||||
alloc_clear(&minor2);
|
||||
unit_assert(major.num_quar == 11);
|
||||
alloc_clear(&major);
|
||||
}
|
||||
|
||||
#include "util/net_help.h"
|
||||
/** test net code */
|
||||
static void
|
||||
net_test(void)
|
||||
{
|
||||
const char* t4[] = {"\000\000\000\000",
|
||||
"\200\000\000\000",
|
||||
"\300\000\000\000",
|
||||
"\340\000\000\000",
|
||||
"\360\000\000\000",
|
||||
"\370\000\000\000",
|
||||
"\374\000\000\000",
|
||||
"\376\000\000\000",
|
||||
"\377\000\000\000",
|
||||
"\377\200\000\000",
|
||||
"\377\300\000\000",
|
||||
"\377\340\000\000",
|
||||
"\377\360\000\000",
|
||||
"\377\370\000\000",
|
||||
"\377\374\000\000",
|
||||
"\377\376\000\000",
|
||||
"\377\377\000\000",
|
||||
"\377\377\200\000",
|
||||
"\377\377\300\000",
|
||||
"\377\377\340\000",
|
||||
"\377\377\360\000",
|
||||
"\377\377\370\000",
|
||||
"\377\377\374\000",
|
||||
"\377\377\376\000",
|
||||
"\377\377\377\000",
|
||||
"\377\377\377\200",
|
||||
"\377\377\377\300",
|
||||
"\377\377\377\340",
|
||||
"\377\377\377\360",
|
||||
"\377\377\377\370",
|
||||
"\377\377\377\374",
|
||||
"\377\377\377\376",
|
||||
"\377\377\377\377",
|
||||
"\377\377\377\377",
|
||||
"\377\377\377\377",
|
||||
};
|
||||
unit_show_func("util/net_help.c", "str_is_ip6");
|
||||
unit_assert( str_is_ip6("::") );
|
||||
unit_assert( str_is_ip6("::1") );
|
||||
unit_assert( str_is_ip6("2001:7b8:206:1:240:f4ff:fe37:8810") );
|
||||
unit_assert( str_is_ip6("fe80::240:f4ff:fe37:8810") );
|
||||
unit_assert( !str_is_ip6("0.0.0.0") );
|
||||
unit_assert( !str_is_ip6("213.154.224.12") );
|
||||
unit_assert( !str_is_ip6("213.154.224.255") );
|
||||
unit_assert( !str_is_ip6("255.255.255.0") );
|
||||
unit_show_func("util/net_help.c", "is_pow2");
|
||||
unit_assert( is_pow2(0) );
|
||||
unit_assert( is_pow2(1) );
|
||||
unit_assert( is_pow2(2) );
|
||||
unit_assert( is_pow2(4) );
|
||||
unit_assert( is_pow2(8) );
|
||||
unit_assert( is_pow2(16) );
|
||||
unit_assert( is_pow2(1024) );
|
||||
unit_assert( is_pow2(1024*1024) );
|
||||
unit_assert( is_pow2(1024*1024*1024) );
|
||||
unit_assert( !is_pow2(3) );
|
||||
unit_assert( !is_pow2(5) );
|
||||
unit_assert( !is_pow2(6) );
|
||||
unit_assert( !is_pow2(7) );
|
||||
unit_assert( !is_pow2(9) );
|
||||
unit_assert( !is_pow2(10) );
|
||||
unit_assert( !is_pow2(11) );
|
||||
unit_assert( !is_pow2(17) );
|
||||
unit_assert( !is_pow2(23) );
|
||||
unit_assert( !is_pow2(257) );
|
||||
unit_assert( !is_pow2(259) );
|
||||
|
||||
/* test addr_mask */
|
||||
unit_show_func("util/net_help.c", "addr_mask");
|
||||
if(1) {
|
||||
struct sockaddr_in a4;
|
||||
struct sockaddr_in6 a6;
|
||||
socklen_t l4 = (socklen_t)sizeof(a4);
|
||||
socklen_t l6 = (socklen_t)sizeof(a6);
|
||||
int i;
|
||||
a4.sin_family = AF_INET;
|
||||
a6.sin6_family = AF_INET6;
|
||||
for(i=0; i<35; i++) {
|
||||
/* address 255.255.255.255 */
|
||||
memcpy(&a4.sin_addr, "\377\377\377\377", 4);
|
||||
addr_mask((struct sockaddr_storage*)&a4, l4, i);
|
||||
unit_assert(memcmp(&a4.sin_addr, t4[i], 4) == 0);
|
||||
}
|
||||
memcpy(&a6.sin6_addr, "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377", 16);
|
||||
addr_mask((struct sockaddr_storage*)&a6, l6, 128);
|
||||
unit_assert(memcmp(&a6.sin6_addr, "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377", 16) == 0);
|
||||
addr_mask((struct sockaddr_storage*)&a6, l6, 122);
|
||||
unit_assert(memcmp(&a6.sin6_addr, "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\300", 16) == 0);
|
||||
addr_mask((struct sockaddr_storage*)&a6, l6, 120);
|
||||
unit_assert(memcmp(&a6.sin6_addr, "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\000", 16) == 0);
|
||||
addr_mask((struct sockaddr_storage*)&a6, l6, 64);
|
||||
unit_assert(memcmp(&a6.sin6_addr, "\377\377\377\377\377\377\377\377\000\000\000\000\000\000\000\000", 16) == 0);
|
||||
addr_mask((struct sockaddr_storage*)&a6, l6, 0);
|
||||
unit_assert(memcmp(&a6.sin6_addr, "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", 16) == 0);
|
||||
}
|
||||
|
||||
/* test addr_in_common */
|
||||
unit_show_func("util/net_help.c", "addr_in_common");
|
||||
if(1) {
|
||||
struct sockaddr_in a4, b4;
|
||||
struct sockaddr_in6 a6, b6;
|
||||
socklen_t l4 = (socklen_t)sizeof(a4);
|
||||
socklen_t l6 = (socklen_t)sizeof(a6);
|
||||
int i;
|
||||
a4.sin_family = AF_INET;
|
||||
b4.sin_family = AF_INET;
|
||||
a6.sin6_family = AF_INET6;
|
||||
b6.sin6_family = AF_INET6;
|
||||
memcpy(&a4.sin_addr, "abcd", 4);
|
||||
memcpy(&b4.sin_addr, "abcd", 4);
|
||||
unit_assert(addr_in_common((struct sockaddr_storage*)&a4, 32,
|
||||
(struct sockaddr_storage*)&b4, 32, l4) == 32);
|
||||
unit_assert(addr_in_common((struct sockaddr_storage*)&a4, 34,
|
||||
(struct sockaddr_storage*)&b4, 32, l4) == 32);
|
||||
for(i=0; i<=32; i++) {
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&a4, 32,
|
||||
(struct sockaddr_storage*)&b4, i, l4) == i);
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&a4, i,
|
||||
(struct sockaddr_storage*)&b4, 32, l4) == i);
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&a4, i,
|
||||
(struct sockaddr_storage*)&b4, i, l4) == i);
|
||||
}
|
||||
for(i=0; i<=32; i++) {
|
||||
memcpy(&a4.sin_addr, "\377\377\377\377", 4);
|
||||
memcpy(&b4.sin_addr, t4[i], 4);
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&a4, 32,
|
||||
(struct sockaddr_storage*)&b4, 32, l4) == i);
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&b4, 32,
|
||||
(struct sockaddr_storage*)&a4, 32, l4) == i);
|
||||
}
|
||||
memcpy(&a6.sin6_addr, "abcdefghabcdefgh", 16);
|
||||
memcpy(&b6.sin6_addr, "abcdefghabcdefgh", 16);
|
||||
unit_assert(addr_in_common((struct sockaddr_storage*)&a6, 128,
|
||||
(struct sockaddr_storage*)&b6, 128, l6) == 128);
|
||||
unit_assert(addr_in_common((struct sockaddr_storage*)&a6, 129,
|
||||
(struct sockaddr_storage*)&b6, 128, l6) == 128);
|
||||
for(i=0; i<=128; i++) {
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&a6, 128,
|
||||
(struct sockaddr_storage*)&b6, i, l6) == i);
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&a6, i,
|
||||
(struct sockaddr_storage*)&b6, 128, l6) == i);
|
||||
unit_assert(addr_in_common(
|
||||
(struct sockaddr_storage*)&a6, i,
|
||||
(struct sockaddr_storage*)&b6, i, l6) == i);
|
||||
}
|
||||
}
|
||||
/* test sockaddr_cmp_addr */
|
||||
unit_show_func("util/net_help.c", "sockaddr_cmp_addr");
|
||||
if(1) {
|
||||
struct sockaddr_storage a, b;
|
||||
socklen_t alen = (socklen_t)sizeof(a);
|
||||
socklen_t blen = (socklen_t)sizeof(b);
|
||||
unit_assert(ipstrtoaddr("127.0.0.0", 53, &a, &alen));
|
||||
unit_assert(ipstrtoaddr("127.255.255.255", 53, &b, &blen));
|
||||
unit_assert(sockaddr_cmp_addr(&a, alen, &b, blen) < 0);
|
||||
unit_assert(sockaddr_cmp_addr(&b, blen, &a, alen) > 0);
|
||||
unit_assert(sockaddr_cmp_addr(&a, alen, &a, alen) == 0);
|
||||
unit_assert(sockaddr_cmp_addr(&b, blen, &b, blen) == 0);
|
||||
unit_assert(ipstrtoaddr("192.168.121.5", 53, &a, &alen));
|
||||
unit_assert(sockaddr_cmp_addr(&a, alen, &b, blen) > 0);
|
||||
unit_assert(sockaddr_cmp_addr(&b, blen, &a, alen) < 0);
|
||||
unit_assert(sockaddr_cmp_addr(&a, alen, &a, alen) == 0);
|
||||
unit_assert(ipstrtoaddr("2001:3578:ffeb::99", 53, &b, &blen));
|
||||
unit_assert(sockaddr_cmp_addr(&b, blen, &b, blen) == 0);
|
||||
unit_assert(sockaddr_cmp_addr(&a, alen, &b, blen) < 0);
|
||||
unit_assert(sockaddr_cmp_addr(&b, blen, &a, alen) > 0);
|
||||
}
|
||||
/* test addr_is_ip4mapped */
|
||||
unit_show_func("util/net_help.c", "addr_is_ip4mapped");
|
||||
if(1) {
|
||||
struct sockaddr_storage a;
|
||||
socklen_t l = (socklen_t)sizeof(a);
|
||||
unit_assert(ipstrtoaddr("12.13.14.15", 53, &a, &l));
|
||||
unit_assert(!addr_is_ip4mapped(&a, l));
|
||||
unit_assert(ipstrtoaddr("fe80::217:31ff:fe91:df", 53, &a, &l));
|
||||
unit_assert(!addr_is_ip4mapped(&a, l));
|
||||
unit_assert(ipstrtoaddr("ffff::217:31ff:fe91:df", 53, &a, &l));
|
||||
unit_assert(!addr_is_ip4mapped(&a, l));
|
||||
unit_assert(ipstrtoaddr("::ffff:31ff:fe91:df", 53, &a, &l));
|
||||
unit_assert(!addr_is_ip4mapped(&a, l));
|
||||
unit_assert(ipstrtoaddr("::fffe:fe91:df", 53, &a, &l));
|
||||
unit_assert(!addr_is_ip4mapped(&a, l));
|
||||
unit_assert(ipstrtoaddr("::ffff:127.0.0.1", 53, &a, &l));
|
||||
unit_assert(addr_is_ip4mapped(&a, l));
|
||||
unit_assert(ipstrtoaddr("::ffff:127.0.0.2", 53, &a, &l));
|
||||
unit_assert(addr_is_ip4mapped(&a, l));
|
||||
unit_assert(ipstrtoaddr("::ffff:192.168.0.2", 53, &a, &l));
|
||||
unit_assert(addr_is_ip4mapped(&a, l));
|
||||
unit_assert(ipstrtoaddr("2::ffff:192.168.0.2", 53, &a, &l));
|
||||
unit_assert(!addr_is_ip4mapped(&a, l));
|
||||
}
|
||||
/* test addr_is_any */
|
||||
unit_show_func("util/net_help.c", "addr_is_any");
|
||||
if(1) {
|
||||
struct sockaddr_storage a;
|
||||
socklen_t l = (socklen_t)sizeof(a);
|
||||
unit_assert(ipstrtoaddr("0.0.0.0", 53, &a, &l));
|
||||
unit_assert(addr_is_any(&a, l));
|
||||
unit_assert(ipstrtoaddr("0.0.0.0", 10053, &a, &l));
|
||||
unit_assert(addr_is_any(&a, l));
|
||||
unit_assert(ipstrtoaddr("0.0.0.0", 0, &a, &l));
|
||||
unit_assert(addr_is_any(&a, l));
|
||||
unit_assert(ipstrtoaddr("::0", 0, &a, &l));
|
||||
unit_assert(addr_is_any(&a, l));
|
||||
unit_assert(ipstrtoaddr("::0", 53, &a, &l));
|
||||
unit_assert(addr_is_any(&a, l));
|
||||
unit_assert(ipstrtoaddr("::1", 53, &a, &l));
|
||||
unit_assert(!addr_is_any(&a, l));
|
||||
unit_assert(ipstrtoaddr("2001:1667::1", 0, &a, &l));
|
||||
unit_assert(!addr_is_any(&a, l));
|
||||
unit_assert(ipstrtoaddr("2001::0", 0, &a, &l));
|
||||
unit_assert(!addr_is_any(&a, l));
|
||||
unit_assert(ipstrtoaddr("10.0.0.0", 0, &a, &l));
|
||||
unit_assert(!addr_is_any(&a, l));
|
||||
unit_assert(ipstrtoaddr("0.0.0.10", 0, &a, &l));
|
||||
unit_assert(!addr_is_any(&a, l));
|
||||
unit_assert(ipstrtoaddr("192.0.2.1", 0, &a, &l));
|
||||
unit_assert(!addr_is_any(&a, l));
|
||||
}
|
||||
}
|
||||
|
||||
#include "util/config_file.h"
|
||||
/** test config_file: cfg_parse_memsize */
|
||||
static void
|
||||
config_memsize_test(void)
|
||||
{
|
||||
size_t v = 0;
|
||||
unit_show_func("util/config_file.c", "cfg_parse_memsize");
|
||||
if(0) {
|
||||
/* these emit errors */
|
||||
unit_assert( cfg_parse_memsize("", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("bla", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("nop", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("n0b", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("gb", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("b", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("kb", &v) == 0);
|
||||
unit_assert( cfg_parse_memsize("kk kb", &v) == 0);
|
||||
}
|
||||
unit_assert( cfg_parse_memsize("0", &v) && v==0);
|
||||
unit_assert( cfg_parse_memsize("1", &v) && v==1);
|
||||
unit_assert( cfg_parse_memsize("10", &v) && v==10);
|
||||
unit_assert( cfg_parse_memsize("10b", &v) && v==10);
|
||||
unit_assert( cfg_parse_memsize("5b", &v) && v==5);
|
||||
unit_assert( cfg_parse_memsize("1024", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("1k", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("1K", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("1Kb", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("1kb", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("1 kb", &v) && v==1024);
|
||||
unit_assert( cfg_parse_memsize("10 kb", &v) && v==10240);
|
||||
unit_assert( cfg_parse_memsize("2k", &v) && v==2048);
|
||||
unit_assert( cfg_parse_memsize("2m", &v) && v==2048*1024);
|
||||
unit_assert( cfg_parse_memsize("3M", &v) && v==3072*1024);
|
||||
unit_assert( cfg_parse_memsize("40m", &v) && v==40960*1024);
|
||||
unit_assert( cfg_parse_memsize("1G", &v) && v==1024*1024*1024);
|
||||
unit_assert( cfg_parse_memsize("1 Gb", &v) && v==1024*1024*1024);
|
||||
unit_assert( cfg_parse_memsize("0 Gb", &v) && v==0*1024*1024);
|
||||
}
|
||||
|
||||
#include "util/rtt.h"
|
||||
/** test RTT code */
|
||||
static void
|
||||
rtt_test(void)
|
||||
{
|
||||
int init = 376;
|
||||
int i;
|
||||
struct rtt_info r;
|
||||
unit_show_func("util/rtt.c", "rtt_timeout");
|
||||
rtt_init(&r);
|
||||
/* initial value sensible */
|
||||
unit_assert( rtt_timeout(&r) == init );
|
||||
rtt_lost(&r, init);
|
||||
unit_assert( rtt_timeout(&r) == init*2 );
|
||||
rtt_lost(&r, init*2);
|
||||
unit_assert( rtt_timeout(&r) == init*4 );
|
||||
rtt_update(&r, 4000);
|
||||
unit_assert( rtt_timeout(&r) >= 2000 );
|
||||
rtt_lost(&r, rtt_timeout(&r) );
|
||||
for(i=0; i<100; i++) {
|
||||
rtt_lost(&r, rtt_timeout(&r) );
|
||||
unit_assert( rtt_timeout(&r) > RTT_MIN_TIMEOUT-1);
|
||||
unit_assert( rtt_timeout(&r) < RTT_MAX_TIMEOUT+1);
|
||||
}
|
||||
}
|
||||
|
||||
#include "services/cache/infra.h"
|
||||
#include "util/config_file.h"
|
||||
|
||||
/* lookup and get key and data structs easily */
|
||||
static struct infra_data* infra_lookup_host(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
|
||||
size_t zonelen, int wr, time_t now, struct infra_key** k)
|
||||
{
|
||||
struct infra_data* d;
|
||||
struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
|
||||
zone, zonelen, wr);
|
||||
if(!e) return NULL;
|
||||
d = (struct infra_data*)e->data;
|
||||
if(d->ttl < now) {
|
||||
lock_rw_unlock(&e->lock);
|
||||
return NULL;
|
||||
}
|
||||
*k = (struct infra_key*)e->key;
|
||||
return d;
|
||||
}
|
||||
|
||||
/** test host cache */
|
||||
static void
|
||||
infra_test(void)
|
||||
{
|
||||
struct sockaddr_storage one;
|
||||
socklen_t onelen;
|
||||
uint8_t* zone = (uint8_t*)"\007example\003com\000";
|
||||
size_t zonelen = 13;
|
||||
struct infra_cache* slab;
|
||||
struct config_file* cfg = config_create();
|
||||
time_t now = 0;
|
||||
uint8_t edns_lame;
|
||||
int vs, to;
|
||||
struct infra_key* k;
|
||||
struct infra_data* d;
|
||||
int init = 376;
|
||||
|
||||
unit_show_feature("infra cache");
|
||||
unit_assert(ipstrtoaddr("127.0.0.1", 53, &one, &onelen));
|
||||
|
||||
slab = infra_create(cfg);
|
||||
unit_assert( infra_host(slab, &one, onelen, zone, zonelen, now,
|
||||
&vs, &edns_lame, &to) );
|
||||
unit_assert( vs == 0 && to == init && edns_lame == 0 );
|
||||
|
||||
unit_assert( infra_rtt_update(slab, &one, onelen, zone, zonelen, LDNS_RR_TYPE_A, -1, init, now) );
|
||||
unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
|
||||
now, &vs, &edns_lame, &to) );
|
||||
unit_assert( vs == 0 && to == init*2 && edns_lame == 0 );
|
||||
|
||||
unit_assert( infra_edns_update(slab, &one, onelen, zone, zonelen, -1, now) );
|
||||
unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
|
||||
now, &vs, &edns_lame, &to) );
|
||||
unit_assert( vs == -1 && to == init*2 && edns_lame == 1);
|
||||
|
||||
now += cfg->host_ttl + 10;
|
||||
unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
|
||||
now, &vs, &edns_lame, &to) );
|
||||
unit_assert( vs == 0 && to == init && edns_lame == 0 );
|
||||
|
||||
unit_assert( infra_set_lame(slab, &one, onelen,
|
||||
zone, zonelen, now, 0, 0, LDNS_RR_TYPE_A) );
|
||||
unit_assert( (d=infra_lookup_host(slab, &one, onelen, zone, zonelen, 0, now, &k)) );
|
||||
unit_assert( d->ttl == now+cfg->host_ttl );
|
||||
unit_assert( d->edns_version == 0 );
|
||||
unit_assert(!d->isdnsseclame && !d->rec_lame && d->lame_type_A &&
|
||||
!d->lame_other);
|
||||
lock_rw_unlock(&k->entry.lock);
|
||||
|
||||
/* test merge of data */
|
||||
unit_assert( infra_set_lame(slab, &one, onelen,
|
||||
zone, zonelen, now, 0, 0, LDNS_RR_TYPE_AAAA) );
|
||||
unit_assert( (d=infra_lookup_host(slab, &one, onelen, zone, zonelen, 0, now, &k)) );
|
||||
unit_assert(!d->isdnsseclame && !d->rec_lame && d->lame_type_A &&
|
||||
d->lame_other);
|
||||
lock_rw_unlock(&k->entry.lock);
|
||||
|
||||
/* test that noEDNS cannot overwrite known-yesEDNS */
|
||||
now += cfg->host_ttl + 10;
|
||||
unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
|
||||
now, &vs, &edns_lame, &to) );
|
||||
unit_assert( vs == 0 && to == init && edns_lame == 0 );
|
||||
|
||||
unit_assert( infra_edns_update(slab, &one, onelen, zone, zonelen, 0, now) );
|
||||
unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
|
||||
now, &vs, &edns_lame, &to) );
|
||||
unit_assert( vs == 0 && to == init && edns_lame == 1 );
|
||||
|
||||
unit_assert( infra_edns_update(slab, &one, onelen, zone, zonelen, -1, now) );
|
||||
unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
|
||||
now, &vs, &edns_lame, &to) );
|
||||
unit_assert( vs == 0 && to == init && edns_lame == 1 );
|
||||
|
||||
infra_delete(slab);
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
#include "util/random.h"
|
||||
/** test randomness */
|
||||
static void
|
||||
rnd_test(void)
|
||||
{
|
||||
struct ub_randstate* r;
|
||||
int num = 1000, i;
|
||||
long int a[1000];
|
||||
unsigned int seed = (unsigned)time(NULL);
|
||||
unit_show_feature("ub_random");
|
||||
printf("ub_random seed is %u\n", seed);
|
||||
unit_assert( (r = ub_initstate(seed, NULL)) );
|
||||
for(i=0; i<num; i++) {
|
||||
a[i] = ub_random(r);
|
||||
unit_assert(a[i] >= 0);
|
||||
unit_assert((size_t)a[i] <= (size_t)0x7fffffff);
|
||||
if(i > 5)
|
||||
unit_assert(a[i] != a[i-1] || a[i] != a[i-2] ||
|
||||
a[i] != a[i-3] || a[i] != a[i-4] ||
|
||||
a[i] != a[i-5] || a[i] != a[i-6]);
|
||||
}
|
||||
a[0] = ub_random_max(r, 1);
|
||||
unit_assert(a[0] >= 0 && a[0] < 1);
|
||||
a[0] = ub_random_max(r, 10000);
|
||||
unit_assert(a[0] >= 0 && a[0] < 10000);
|
||||
for(i=0; i<num; i++) {
|
||||
a[i] = ub_random_max(r, 10);
|
||||
unit_assert(a[i] >= 0 && a[i] < 10);
|
||||
}
|
||||
ub_randfree(r);
|
||||
}
|
||||
|
||||
void unit_show_func(const char* file, const char* func)
|
||||
{
|
||||
printf("test %s:%s\n", file, func);
|
||||
}
|
||||
|
||||
void unit_show_feature(const char* feature)
|
||||
{
|
||||
printf("test %s functions\n", feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main unit test program. Setup, teardown and report errors.
|
||||
* @param argc: arg count.
|
||||
* @param argv: array of commandline arguments.
|
||||
* @return program failure if test fails.
|
||||
*/
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
log_init(NULL, 0, NULL);
|
||||
if(argc != 1) {
|
||||
printf("usage: %s\n", argv[0]);
|
||||
printf("\tperforms unit tests.\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Start of %s unit test.\n", PACKAGE_STRING);
|
||||
#ifdef HAVE_SSL
|
||||
ERR_load_crypto_strings();
|
||||
# ifdef HAVE_OPENSSL_CONFIG
|
||||
OPENSSL_config("unbound");
|
||||
# endif
|
||||
# ifdef USE_GOST
|
||||
(void)sldns_key_EVP_load_gost_id();
|
||||
# endif
|
||||
#elif defined(HAVE_NSS)
|
||||
if(NSS_NoDB_Init(".") != SECSuccess)
|
||||
fatal_exit("could not init NSS");
|
||||
#endif /* HAVE_SSL or HAVE_NSS*/
|
||||
checklock_start();
|
||||
neg_test();
|
||||
rnd_test();
|
||||
verify_test();
|
||||
net_test();
|
||||
config_memsize_test();
|
||||
dname_test();
|
||||
rtt_test();
|
||||
anchors_test();
|
||||
alloc_test();
|
||||
regional_test();
|
||||
lruhash_test();
|
||||
slabhash_test();
|
||||
infra_test();
|
||||
ldns_test();
|
||||
msgparse_test();
|
||||
checklock_stop();
|
||||
printf("%d checks ok.\n", testcount);
|
||||
#ifdef HAVE_SSL
|
||||
# if defined(USE_GOST) && defined(HAVE_LDNS_KEY_EVP_UNLOAD_GOST)
|
||||
sldns_key_EVP_unload_gost();
|
||||
# endif
|
||||
# ifdef HAVE_OPENSSL_CONFIG
|
||||
EVP_cleanup();
|
||||
ENGINE_cleanup();
|
||||
CONF_modules_free();
|
||||
# endif
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
ERR_remove_state(0);
|
||||
ERR_free_strings();
|
||||
RAND_cleanup();
|
||||
#elif defined(HAVE_NSS)
|
||||
if(NSS_Shutdown() != SECSuccess)
|
||||
fatal_exit("could not shutdown NSS");
|
||||
#endif /* HAVE_SSL or HAVE_NSS */
|
||||
#ifdef HAVE_PTHREAD
|
||||
/* dlopen frees its thread specific state */
|
||||
pthread_exit(NULL);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
78
external/unbound/testcode/unitmain.h
vendored
Normal file
78
external/unbound/testcode/unitmain.h
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* testcode/unitmain.h - unit test main program for unbound.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* \file
|
||||
* Declarations useful for the unit tests.
|
||||
*/
|
||||
|
||||
#ifndef TESTCODE_UNITMAIN_H
|
||||
#define TESTCODE_UNITMAIN_H
|
||||
#include "util/log.h"
|
||||
|
||||
/** number of tests done */
|
||||
extern int testcount;
|
||||
/** test bool x, exits on failure, increases testcount. */
|
||||
#ifdef DEBUG_UNBOUND
|
||||
#define unit_assert(x) do {testcount++; log_assert(x);} while(0)
|
||||
#else
|
||||
#define unit_assert(x) do {testcount++; if(!(x)) { fprintf(stderr, "assertion failure %s:%d\n", __FILE__, __LINE__); exit(1);}} while(0)
|
||||
#endif
|
||||
|
||||
/** we are now testing this function */
|
||||
void unit_show_func(const char* file, const char* func);
|
||||
/** we are testing this functionality */
|
||||
void unit_show_feature(const char* feature);
|
||||
|
||||
/** unit test lruhashtable implementation */
|
||||
void lruhash_test(void);
|
||||
/** unit test slabhashtable implementation */
|
||||
void slabhash_test(void);
|
||||
/** unit test for msgreply and msgparse */
|
||||
void msgparse_test(void);
|
||||
/** unit test dname handling functions */
|
||||
void dname_test(void);
|
||||
/** unit test trust anchor storage functions */
|
||||
void anchors_test(void);
|
||||
/** unit test for verification functions */
|
||||
void verify_test(void);
|
||||
/** unit test for negative cache functions */
|
||||
void neg_test(void);
|
||||
/** unit test for regional allocator functions */
|
||||
void regional_test(void);
|
||||
/** unit test for ldns functions */
|
||||
void ldns_test(void);
|
||||
|
||||
#endif /* TESTCODE_UNITMAIN_H */
|
||||
539
external/unbound/testcode/unitmsgparse.c
vendored
Normal file
539
external/unbound/testcode/unitmsgparse.c
vendored
Normal file
|
|
@ -0,0 +1,539 @@
|
|||
/*
|
||||
* testcode/unitmsgparse.c - unit test for msg parse routines.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* \file
|
||||
* Calls msg parse unit tests. Exits with code 1 on a failure.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <sys/time.h>
|
||||
#include "util/log.h"
|
||||
#include "testcode/unitmain.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/msgencode.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/alloc.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/net_help.h"
|
||||
#include "testcode/readhex.h"
|
||||
#include "testcode/testpkts.h"
|
||||
#include "ldns/sbuffer.h"
|
||||
#include "ldns/str2wire.h"
|
||||
#include "ldns/wire2str.h"
|
||||
|
||||
/** verbose message parse unit test */
|
||||
static int vbmp = 0;
|
||||
/** do not accept formerr */
|
||||
static int check_formerr_gone = 0;
|
||||
/** if matching within a section should disregard the order of RRs. */
|
||||
static int matches_nolocation = 0;
|
||||
/** see if RRSIGs are properly matched to RRsets. */
|
||||
static int check_rrsigs = 0;
|
||||
/** do not check buffer sameness */
|
||||
static int check_nosameness = 0;
|
||||
|
||||
/** see if buffers contain the same packet */
|
||||
static int
|
||||
test_buffers(sldns_buffer* pkt, sldns_buffer* out)
|
||||
{
|
||||
/* check binary same */
|
||||
if(sldns_buffer_limit(pkt) == sldns_buffer_limit(out) &&
|
||||
memcmp(sldns_buffer_begin(pkt), sldns_buffer_begin(out),
|
||||
sldns_buffer_limit(pkt)) == 0) {
|
||||
if(vbmp) printf("binary the same (length=%u)\n",
|
||||
(unsigned)sldns_buffer_limit(pkt));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(vbmp) {
|
||||
size_t sz = 16;
|
||||
size_t count;
|
||||
size_t lim = sldns_buffer_limit(out);
|
||||
if(sldns_buffer_limit(pkt) < lim)
|
||||
lim = sldns_buffer_limit(pkt);
|
||||
for(count=0; count<lim; count+=sz) {
|
||||
size_t rem = sz;
|
||||
if(lim-count < sz) rem = lim-count;
|
||||
if(memcmp(sldns_buffer_at(pkt, count),
|
||||
sldns_buffer_at(out, count), rem) == 0) {
|
||||
log_info("same %d %d", (int)count, (int)rem);
|
||||
log_hex("same: ", sldns_buffer_at(pkt, count),
|
||||
rem);
|
||||
} else {
|
||||
log_info("diff %d %d", (int)count, (int)rem);
|
||||
log_hex("difp: ", sldns_buffer_at(pkt, count),
|
||||
rem);
|
||||
log_hex("difo: ", sldns_buffer_at(out, count),
|
||||
rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check if it 'means the same' */
|
||||
if(vbmp) {
|
||||
char* s1, *s2;
|
||||
log_buf(0, "orig in hex", pkt);
|
||||
log_buf(0, "unbound out in hex", out);
|
||||
printf("\npacket from unbound (%d):\n",
|
||||
(int)sldns_buffer_limit(out));
|
||||
s1 = sldns_wire2str_pkt(sldns_buffer_begin(out),
|
||||
sldns_buffer_limit(out));
|
||||
printf("%s\n", s1?s1:"null");
|
||||
free(s1);
|
||||
|
||||
printf("\npacket original (%d):\n",
|
||||
(int)sldns_buffer_limit(pkt));
|
||||
s2 = sldns_wire2str_pkt(sldns_buffer_begin(pkt),
|
||||
sldns_buffer_limit(pkt));
|
||||
printf("%s\n", s2?s2:"null");
|
||||
free(s2);
|
||||
printf("\n");
|
||||
}
|
||||
/* if it had two EDNS sections, skip comparison */
|
||||
if(1) {
|
||||
char* s = sldns_wire2str_pkt(sldns_buffer_begin(pkt),
|
||||
sldns_buffer_limit(pkt));
|
||||
char* e1 = strstr(s, "; EDNS:");
|
||||
if(e1 && strstr(e1+4, "; EDNS:")) {
|
||||
free(s);
|
||||
return 0;
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
/* compare packets */
|
||||
unit_assert(match_all(sldns_buffer_begin(pkt), sldns_buffer_limit(pkt),
|
||||
sldns_buffer_begin(out), sldns_buffer_limit(out), 1,
|
||||
matches_nolocation));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** check if unbound formerr equals ldns formerr */
|
||||
static void
|
||||
checkformerr(sldns_buffer* pkt)
|
||||
{
|
||||
int status = 0;
|
||||
char* s = sldns_wire2str_pkt(sldns_buffer_begin(pkt),
|
||||
sldns_buffer_limit(pkt));
|
||||
if(!s) fatal_exit("out of memory");
|
||||
if(strstr(s, "Error")) status = 1;
|
||||
if(strstr(s, "error")) status = 1;
|
||||
if(status == 0) {
|
||||
printf("Formerr, but ldns gives packet:\n");
|
||||
printf("%s\n", s);
|
||||
free(s);
|
||||
exit(1);
|
||||
}
|
||||
free(s);
|
||||
unit_assert(status != 0);
|
||||
}
|
||||
|
||||
/** performance test message encoding */
|
||||
static void
|
||||
perf_encode(struct query_info* qi, struct reply_info* rep, uint16_t id,
|
||||
uint16_t flags, sldns_buffer* out, time_t timenow,
|
||||
struct edns_data* edns)
|
||||
{
|
||||
static int num = 0;
|
||||
int ret;
|
||||
size_t max = 10000;
|
||||
size_t i;
|
||||
struct timeval start, end;
|
||||
double dt;
|
||||
struct regional* r2 = regional_create();
|
||||
if(gettimeofday(&start, NULL) < 0)
|
||||
fatal_exit("gettimeofday: %s", strerror(errno));
|
||||
/* encode a couple times */
|
||||
for(i=0; i<max; i++) {
|
||||
ret = reply_info_encode(qi, rep, id, flags, out, timenow,
|
||||
r2, 65535, (int)(edns->bits & EDNS_DO) );
|
||||
unit_assert(ret != 0); /* udp packets should fit */
|
||||
attach_edns_record(out, edns);
|
||||
regional_free_all(r2);
|
||||
}
|
||||
if(gettimeofday(&end, NULL) < 0)
|
||||
fatal_exit("gettimeofday: %s", strerror(errno));
|
||||
/* time in millisec */
|
||||
dt = (double)(end.tv_sec - start.tv_sec)*1000. +
|
||||
((double)end.tv_usec - (double)start.tv_usec)/1000.;
|
||||
printf("[%d] did %u in %g msec for %f encode/sec size %d\n", num++,
|
||||
(unsigned)max, dt, (double)max / (dt/1000.),
|
||||
(int)sldns_buffer_limit(out));
|
||||
regional_destroy(r2);
|
||||
}
|
||||
|
||||
/** perf test a packet */
|
||||
static void
|
||||
perftestpkt(sldns_buffer* pkt, struct alloc_cache* alloc, sldns_buffer* out,
|
||||
const char* hex)
|
||||
{
|
||||
struct query_info qi;
|
||||
struct reply_info* rep = 0;
|
||||
int ret;
|
||||
uint16_t id;
|
||||
uint16_t flags;
|
||||
time_t timenow = 0;
|
||||
struct regional* region = regional_create();
|
||||
struct edns_data edns;
|
||||
|
||||
hex_to_buf(pkt, hex);
|
||||
memmove(&id, sldns_buffer_begin(pkt), sizeof(id));
|
||||
if(sldns_buffer_limit(pkt) < 2)
|
||||
flags = 0;
|
||||
else memmove(&flags, sldns_buffer_at(pkt, 2), sizeof(flags));
|
||||
flags = ntohs(flags);
|
||||
ret = reply_info_parse(pkt, alloc, &qi, &rep, region, &edns);
|
||||
if(ret != 0) {
|
||||
char rbuf[16];
|
||||
sldns_wire2str_rcode_buf(ret, rbuf, sizeof(rbuf));
|
||||
if(vbmp) printf("parse code %d: %s\n", ret, rbuf);
|
||||
if(ret == LDNS_RCODE_FORMERR)
|
||||
checkformerr(pkt);
|
||||
unit_assert(ret != LDNS_RCODE_SERVFAIL);
|
||||
} else {
|
||||
perf_encode(&qi, rep, id, flags, out, timenow, &edns);
|
||||
}
|
||||
|
||||
query_info_clear(&qi);
|
||||
reply_info_parsedelete(rep, alloc);
|
||||
regional_destroy(region);
|
||||
}
|
||||
|
||||
/** print packed rrset */
|
||||
static void
|
||||
print_rrset(struct ub_packed_rrset_key* rrset)
|
||||
{
|
||||
struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
|
||||
entry.data;
|
||||
char buf[65535];
|
||||
size_t i;
|
||||
for(i=0; i<d->count+d->rrsig_count; i++) {
|
||||
if(!packed_rr_to_string(rrset, i, 0, buf, sizeof(buf)))
|
||||
printf("failedtoconvert %d\n", (int)i);
|
||||
else
|
||||
printf("%s\n", buf);
|
||||
}
|
||||
}
|
||||
|
||||
/** debug print a packet that failed */
|
||||
static void
|
||||
print_packet_rrsets(struct query_info* qinfo, struct reply_info* rep)
|
||||
{
|
||||
size_t i;
|
||||
log_query_info(0, "failed query", qinfo);
|
||||
printf(";; ANSWER SECTION (%d rrsets)\n", (int)rep->an_numrrsets);
|
||||
for(i=0; i<rep->an_numrrsets; i++) {
|
||||
printf("; rrset %d\n", (int)i);
|
||||
print_rrset(rep->rrsets[i]);
|
||||
}
|
||||
printf(";; AUTHORITY SECTION (%d rrsets)\n", (int)rep->ns_numrrsets);
|
||||
for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) {
|
||||
printf("; rrset %d\n", (int)i);
|
||||
print_rrset(rep->rrsets[i]);
|
||||
}
|
||||
printf(";; ADDITIONAL SECTION (%d rrsets)\n", (int)rep->ar_numrrsets);
|
||||
for(i=rep->an_numrrsets+rep->ns_numrrsets; i<rep->rrset_count; i++) {
|
||||
printf("; rrset %d\n", (int)i);
|
||||
print_rrset(rep->rrsets[i]);
|
||||
}
|
||||
printf(";; packet end\n");
|
||||
}
|
||||
|
||||
/** check that there is no data element that matches the RRSIG */
|
||||
static int
|
||||
no_data_for_rrsig(struct reply_info* rep, struct ub_packed_rrset_key* rrsig)
|
||||
{
|
||||
size_t i;
|
||||
for(i=0; i<rep->rrset_count; i++) {
|
||||
if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_RRSIG)
|
||||
continue;
|
||||
if(query_dname_compare(rep->rrsets[i]->rk.dname,
|
||||
rrsig->rk.dname) == 0)
|
||||
/* only name is compared right now */
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** check RRSIGs in packet */
|
||||
static void
|
||||
check_the_rrsigs(struct query_info* qinfo, struct reply_info* rep)
|
||||
{
|
||||
/* every RRSIG must be matched to an RRset */
|
||||
size_t i;
|
||||
for(i=0; i<rep->rrset_count; i++) {
|
||||
struct ub_packed_rrset_key* s = rep->rrsets[i];
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_RRSIG) {
|
||||
/* see if really a problem, i.e. is there a data
|
||||
* element. */
|
||||
if(no_data_for_rrsig(rep, rep->rrsets[i]))
|
||||
continue;
|
||||
log_dns_msg("rrsig failed for packet", qinfo, rep);
|
||||
print_packet_rrsets(qinfo, rep);
|
||||
printf("failed rrset is nr %d\n", (int)i);
|
||||
unit_assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** test a packet */
|
||||
static void
|
||||
testpkt(sldns_buffer* pkt, struct alloc_cache* alloc, sldns_buffer* out,
|
||||
const char* hex)
|
||||
{
|
||||
struct query_info qi;
|
||||
struct reply_info* rep = 0;
|
||||
int ret;
|
||||
uint16_t id;
|
||||
uint16_t flags;
|
||||
uint32_t timenow = 0;
|
||||
struct regional* region = regional_create();
|
||||
struct edns_data edns;
|
||||
|
||||
hex_to_buf(pkt, hex);
|
||||
memmove(&id, sldns_buffer_begin(pkt), sizeof(id));
|
||||
if(sldns_buffer_limit(pkt) < 2)
|
||||
flags = 0;
|
||||
else memmove(&flags, sldns_buffer_at(pkt, 2), sizeof(flags));
|
||||
flags = ntohs(flags);
|
||||
ret = reply_info_parse(pkt, alloc, &qi, &rep, region, &edns);
|
||||
if(ret != 0) {
|
||||
char rbuf[16];
|
||||
sldns_wire2str_rcode_buf(ret, rbuf, sizeof(rbuf));
|
||||
if(vbmp) printf("parse code %d: %s\n", ret, rbuf);
|
||||
if(ret == LDNS_RCODE_FORMERR) {
|
||||
unit_assert(!check_formerr_gone);
|
||||
checkformerr(pkt);
|
||||
}
|
||||
unit_assert(ret != LDNS_RCODE_SERVFAIL);
|
||||
} else if(!check_formerr_gone) {
|
||||
const size_t lim = 512;
|
||||
ret = reply_info_encode(&qi, rep, id, flags, out, timenow,
|
||||
region, 65535, (int)(edns.bits & EDNS_DO) );
|
||||
unit_assert(ret != 0); /* udp packets should fit */
|
||||
attach_edns_record(out, &edns);
|
||||
if(vbmp) printf("inlen %u outlen %u\n",
|
||||
(unsigned)sldns_buffer_limit(pkt),
|
||||
(unsigned)sldns_buffer_limit(out));
|
||||
if(!check_nosameness)
|
||||
test_buffers(pkt, out);
|
||||
if(check_rrsigs)
|
||||
check_the_rrsigs(&qi, rep);
|
||||
|
||||
if(sldns_buffer_limit(out) > lim) {
|
||||
ret = reply_info_encode(&qi, rep, id, flags, out,
|
||||
timenow, region,
|
||||
lim - calc_edns_field_size(&edns),
|
||||
(int)(edns.bits & EDNS_DO));
|
||||
unit_assert(ret != 0); /* should fit, but with TC */
|
||||
attach_edns_record(out, &edns);
|
||||
if( LDNS_QDCOUNT(sldns_buffer_begin(out)) !=
|
||||
LDNS_QDCOUNT(sldns_buffer_begin(pkt)) ||
|
||||
LDNS_ANCOUNT(sldns_buffer_begin(out)) !=
|
||||
LDNS_ANCOUNT(sldns_buffer_begin(pkt)) ||
|
||||
LDNS_NSCOUNT(sldns_buffer_begin(out)) !=
|
||||
LDNS_NSCOUNT(sldns_buffer_begin(pkt)))
|
||||
unit_assert(
|
||||
LDNS_TC_WIRE(sldns_buffer_begin(out)));
|
||||
/* must set TC bit if shortened */
|
||||
unit_assert(sldns_buffer_limit(out) <= lim);
|
||||
}
|
||||
}
|
||||
|
||||
query_info_clear(&qi);
|
||||
reply_info_parsedelete(rep, alloc);
|
||||
regional_destroy(region);
|
||||
}
|
||||
|
||||
/** simple test of parsing */
|
||||
static void
|
||||
simpletest(sldns_buffer* pkt, struct alloc_cache* alloc, sldns_buffer* out)
|
||||
{
|
||||
/* a root query drill -q - */
|
||||
testpkt(pkt, alloc, out,
|
||||
" c5 40 01 00 00 01 00 00 00 00 00 00 00 00 02 00 01 ");
|
||||
|
||||
/* very small packet */
|
||||
testpkt(pkt, alloc, out,
|
||||
"; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19\n"
|
||||
";-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --\n"
|
||||
"74 0c 85 83 00 01 00 00 00 01 00 00 03 62 6c 61 09 6e 6c 6e ; 1- 20\n"
|
||||
"65 74 6c 61 62 73 02 6e 6c 00 00 0f 00 01 09 6e 6c 6e 65 74 ; 21- 40\n"
|
||||
"6c 61 62 73 02 6e 6c 00 00 06 00 01 00 00 46 50 00 40 04 6f ; 41- 60\n"
|
||||
"70 65 6e 09 6e 6c 6e 65 74 6c 61 62 73 02 6e 6c 00 0a 68 6f ; 61- 80\n"
|
||||
"73 74 6d 61 73 74 65 72 09 6e 6c 6e 65 74 6c 61 62 73 02 6e ; 81- 100\n"
|
||||
"6c 00 77 a1 02 58 00 00 70 80 00 00 1c 20 00 09 3a 80 00 00 ; 101- 120\n"
|
||||
"46 50\n");
|
||||
|
||||
/* a root reply drill -w - */
|
||||
testpkt(pkt, alloc, out,
|
||||
" ; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19\n"
|
||||
" ;-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --\n"
|
||||
" 97 3f 81 80 00 01 00 0d 00 00 00 02 00 00 02 00 01 00 00 02 ; 1- 20\n"
|
||||
" 00 01 00 06 6d 38 00 14 01 49 0c 52 4f 4f 54 2d 53 45 52 56 ; 21- 40\n"
|
||||
" 45 52 53 03 4e 45 54 00 00 00 02 00 01 00 06 6d 38 00 14 01 ; 41- 60\n"
|
||||
" 4a 0c 52 4f 4f 54 2d 53 45 52 56 45 52 53 03 4e 45 54 00 00 ; 61- 80\n"
|
||||
" 00 02 00 01 00 06 6d 38 00 14 01 4b 0c 52 4f 4f 54 2d 53 45 ; 81- 100\n"
|
||||
" 52 56 45 52 53 03 4e 45 54 00 00 00 02 00 01 00 06 6d 38 00 ; 101- 120\n"
|
||||
" 14 01 4c 0c 52 4f 4f 54 2d 53 45 52 56 45 52 53 03 4e 45 54 ; 121- 140\n"
|
||||
" 00 00 00 02 00 01 00 06 6d 38 00 14 01 4d 0c 52 4f 4f 54 2d ; 141- 160\n"
|
||||
" 53 45 52 56 45 52 53 03 4e 45 54 00 00 00 02 00 01 00 06 6d ; 161- 180\n"
|
||||
" 38 00 14 01 41 0c 52 4f 4f 54 2d 53 45 52 56 45 52 53 03 4e ; 181- 200\n"
|
||||
" 45 54 00 00 00 02 00 01 00 06 6d 38 00 14 01 42 0c 52 4f 4f ; 201- 220\n"
|
||||
" 54 2d 53 45 52 56 45 52 53 03 4e 45 54 00 00 00 02 00 01 00 ; 221- 240\n"
|
||||
" 06 6d 38 00 14 01 43 0c 52 4f 4f 54 2d 53 45 52 56 45 52 53 ; 241- 260\n"
|
||||
" 03 4e 45 54 00 00 00 02 00 01 00 06 6d 38 00 14 01 44 0c 52 ; 261- 280\n"
|
||||
" 4f 4f 54 2d 53 45 52 56 45 52 53 03 4e 45 54 00 00 00 02 00 ; 281- 300\n"
|
||||
" 01 00 06 6d 38 00 14 01 45 0c 52 4f 4f 54 2d 53 45 52 56 45 ; 301- 320\n"
|
||||
" 52 53 03 4e 45 54 00 00 00 02 00 01 00 06 6d 38 00 14 01 46 ; 321- 340\n"
|
||||
" 0c 52 4f 4f 54 2d 53 45 52 56 45 52 53 03 4e 45 54 00 00 00 ; 341- 360\n"
|
||||
" 02 00 01 00 06 6d 38 00 14 01 47 0c 52 4f 4f 54 2d 53 45 52 ; 361- 380\n"
|
||||
" 56 45 52 53 03 4e 45 54 00 00 00 02 00 01 00 06 6d 38 00 14 ; 381- 400\n"
|
||||
" 01 48 0c 52 4f 4f 54 2d 53 45 52 56 45 52 53 03 4e 45 54 00 ; 401- 420\n"
|
||||
" 01 41 0c 52 4f 4f 54 2d 53 45 52 56 45 52 53 03 4e 45 54 00 ; 421- 440\n"
|
||||
" 00 01 00 01 00 02 64 b9 00 04 c6 29 00 04 01 4a 0c 52 4f 4f ; 441- 460\n"
|
||||
" 54 2d 53 45 52 56 45 52 53 03 4e 45 54 00 00 01 00 01 00 02 ; 461- 480\n"
|
||||
" 64 b9 00 04 c0 3a 80 1e ");
|
||||
|
||||
/* root delegation from unbound trace with new AAAA glue */
|
||||
perftestpkt(pkt, alloc, out,
|
||||
"55BC84000001000D00000014000002000100000200010007E900001401610C726F6F742D73657276657273036E65740000000200010007E90000040162C01E00000200010007E90000040163C01E00000200010007E90000040164C01E00000200010007E90000040165C01E00000200010007E90000040166C01E00000200010007E90000040167C01E00000200010007E90000040168C01E00000200010007E90000040169C01E00000200010007E9000004016AC01E00000200010007E9000004016BC01E00000200010007E9000004016CC01E00000200010007E9000004016DC01EC01C000100010007E9000004C6290004C03B000100010007E9000004C0E44FC9C04A000100010007E9000004C021040CC059000100010007E900000480080A5AC068000100010007E9000004C0CBE60AC077000100010007E9000004C00505F1C086000100010007E9000004C0702404C095000100010007E9000004803F0235C0A4000100010007E9000004C0249411C0B3000100010007E9000004C03A801EC0C2000100010007E9000004C1000E81C0D1000100010007E9000004C707532AC0E0000100010007E9000004CA0C1B21C01C001C00010007E900001020010503BA3E00000000000000020030C077001C00010007E900001020010500002F0000000000000000000FC095001C00010007E90000102001050000010000"
|
||||
"00000000803F0235C0B3001C00010007E9000010200105030C2700000000000000020030C0C2001C00010007E9000010200107FD000000000000000000000001C0E0001C00010007E900001020010DC30000000000000000000000350000291000000000000000"
|
||||
);
|
||||
}
|
||||
|
||||
/** simple test of parsing, pcat file */
|
||||
static void
|
||||
testfromfile(sldns_buffer* pkt, struct alloc_cache* alloc, sldns_buffer* out,
|
||||
const char* fname)
|
||||
{
|
||||
FILE* in = fopen(fname, "r");
|
||||
char buf[102400];
|
||||
int no=0;
|
||||
if(!in) {
|
||||
perror("fname");
|
||||
return;
|
||||
}
|
||||
while(fgets(buf, (int)sizeof(buf), in)) {
|
||||
if(buf[0] == ';') /* comment */
|
||||
continue;
|
||||
if(strlen(buf) < 10) /* skip pcat line numbers. */
|
||||
continue;
|
||||
if(vbmp) {
|
||||
printf("test no %d: %s", no, buf);
|
||||
fflush(stdout);
|
||||
}
|
||||
testpkt(pkt, alloc, out, buf);
|
||||
no++;
|
||||
}
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
/** simple test of parsing, drill file */
|
||||
static void
|
||||
testfromdrillfile(sldns_buffer* pkt, struct alloc_cache* alloc,
|
||||
sldns_buffer* out, const char* fname)
|
||||
{
|
||||
/* ;-- is used to indicate a new message */
|
||||
FILE* in = fopen(fname, "r");
|
||||
char buf[102400];
|
||||
char* np = buf;
|
||||
buf[0]=0;
|
||||
if(!in) {
|
||||
perror("fname");
|
||||
return;
|
||||
}
|
||||
while(fgets(np, (int)sizeof(buf) - (np-buf), in)) {
|
||||
if(strncmp(np, ";--", 3) == 0) {
|
||||
/* new entry */
|
||||
/* test previous */
|
||||
if(np != buf)
|
||||
testpkt(pkt, alloc, out, buf);
|
||||
/* set for new entry */
|
||||
np = buf;
|
||||
buf[0]=0;
|
||||
continue;
|
||||
}
|
||||
if(np[0] == ';') /* comment */
|
||||
continue;
|
||||
np = &np[strlen(np)];
|
||||
}
|
||||
testpkt(pkt, alloc, out, buf);
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
void msgparse_test(void)
|
||||
{
|
||||
sldns_buffer* pkt = sldns_buffer_new(65553);
|
||||
sldns_buffer* out = sldns_buffer_new(65553);
|
||||
struct alloc_cache super_a, alloc;
|
||||
/* init */
|
||||
alloc_init(&super_a, NULL, 0);
|
||||
alloc_init(&alloc, &super_a, 2);
|
||||
|
||||
unit_show_feature("message parse");
|
||||
simpletest(pkt, &alloc, out);
|
||||
/* plain hex dumps, like pcat */
|
||||
testfromfile(pkt, &alloc, out, "testdata/test_packets.1");
|
||||
testfromfile(pkt, &alloc, out, "testdata/test_packets.2");
|
||||
testfromfile(pkt, &alloc, out, "testdata/test_packets.3");
|
||||
/* like from drill -w - */
|
||||
testfromdrillfile(pkt, &alloc, out, "testdata/test_packets.4");
|
||||
testfromdrillfile(pkt, &alloc, out, "testdata/test_packets.5");
|
||||
|
||||
matches_nolocation = 1; /* RR order not important for the next test */
|
||||
testfromdrillfile(pkt, &alloc, out, "testdata/test_packets.6");
|
||||
check_rrsigs = 1;
|
||||
testfromdrillfile(pkt, &alloc, out, "testdata/test_packets.7");
|
||||
check_rrsigs = 0;
|
||||
matches_nolocation = 0;
|
||||
|
||||
check_formerr_gone = 1;
|
||||
testfromdrillfile(pkt, &alloc, out, "testdata/test_packets.8");
|
||||
check_formerr_gone = 0;
|
||||
|
||||
check_rrsigs = 1;
|
||||
check_nosameness = 1;
|
||||
testfromdrillfile(pkt, &alloc, out, "testdata/test_packets.9");
|
||||
check_nosameness = 0;
|
||||
check_rrsigs = 0;
|
||||
|
||||
/* cleanup */
|
||||
alloc_clear(&alloc);
|
||||
alloc_clear(&super_a);
|
||||
sldns_buffer_free(pkt);
|
||||
sldns_buffer_free(out);
|
||||
}
|
||||
543
external/unbound/testcode/unitneg.c
vendored
Normal file
543
external/unbound/testcode/unitneg.c
vendored
Normal file
|
|
@ -0,0 +1,543 @@
|
|||
/*
|
||||
* testcode/unitneg.c - unit test for negative cache routines.
|
||||
*
|
||||
* Copyright (c) 2008, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* \file
|
||||
* Calls negative cache unit tests. Exits with code 1 on a failure.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/data/packed_rrset.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "testcode/unitmain.h"
|
||||
#include "validator/val_neg.h"
|
||||
#include "ldns/rrdef.h"
|
||||
|
||||
/** verbose unit test for negative cache */
|
||||
static int negverbose = 0;
|
||||
|
||||
/** debug printout of neg cache */
|
||||
static void print_neg_cache(struct val_neg_cache* neg)
|
||||
{
|
||||
char buf[1024];
|
||||
struct val_neg_zone* z;
|
||||
struct val_neg_data* d;
|
||||
printf("neg_cache print\n");
|
||||
printf("memuse %d of %d\n", (int)neg->use, (int)neg->max);
|
||||
printf("maxiter %d\n", (int)neg->nsec3_max_iter);
|
||||
printf("%d zones\n", (int)neg->tree.count);
|
||||
RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
|
||||
dname_str(z->name, buf);
|
||||
printf("%24s", buf);
|
||||
printf(" len=%2.2d labs=%d inuse=%d count=%d tree.count=%d\n",
|
||||
(int)z->len, z->labs, (int)z->in_use, z->count,
|
||||
(int)z->tree.count);
|
||||
}
|
||||
RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
|
||||
printf("\n");
|
||||
dname_print(stdout, NULL, z->name);
|
||||
printf(" zone details\n");
|
||||
printf("len=%2.2d labs=%d inuse=%d count=%d tree.count=%d\n",
|
||||
(int)z->len, z->labs, (int)z->in_use, z->count,
|
||||
(int)z->tree.count);
|
||||
if(z->parent) {
|
||||
printf("parent=");
|
||||
dname_print(stdout, NULL, z->parent->name);
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("parent=NULL\n");
|
||||
}
|
||||
|
||||
RBTREE_FOR(d, struct val_neg_data*, &z->tree) {
|
||||
dname_str(d->name, buf);
|
||||
printf("%24s", buf);
|
||||
printf(" len=%2.2d labs=%d inuse=%d count=%d\n",
|
||||
(int)d->len, d->labs, (int)d->in_use, d->count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** get static pointer to random zone name */
|
||||
static char* get_random_zone(void)
|
||||
{
|
||||
static char zname[256];
|
||||
int labels = random() % 3;
|
||||
int i;
|
||||
char* p = zname;
|
||||
int labnum;
|
||||
|
||||
for(i=0; i<labels; i++) {
|
||||
labnum = random()%10;
|
||||
snprintf(p, 256-(p-zname), "\003%3.3d", labnum);
|
||||
p+=4;
|
||||
}
|
||||
snprintf(p, 256-(p-zname), "\007example\003com");
|
||||
return zname;
|
||||
}
|
||||
|
||||
/** get static pointer to random data names from and to */
|
||||
static void get_random_data(char** fromp, char** top, char* zname)
|
||||
{
|
||||
static char buf1[256], buf2[256];
|
||||
int type;
|
||||
int lab1, lab2;
|
||||
int labnum1[10], labnum2[10];
|
||||
int i;
|
||||
char* p;
|
||||
|
||||
*fromp = buf1;
|
||||
*top = buf2;
|
||||
type = random()%10;
|
||||
|
||||
if(type == 0) {
|
||||
/* ENT */
|
||||
lab1 = random() %3 + 1;
|
||||
lab2 = lab1 + random()%3 + 1;
|
||||
for(i=0; i<lab1; i++) {
|
||||
labnum1[i] = random()%100;
|
||||
labnum2[i] = labnum1[i];
|
||||
}
|
||||
for(i=lab1; i<lab2; i++) {
|
||||
labnum2[i] = random()%100;
|
||||
}
|
||||
} else if(type == 1) {
|
||||
/* end of zone */
|
||||
lab2 = 0;
|
||||
lab1 = random()%3 + 1;
|
||||
for(i=0; i<lab1; i++) {
|
||||
labnum1[i] = random()%100;
|
||||
}
|
||||
} else if(type == 2) {
|
||||
/* start of zone */
|
||||
lab1 = 0;
|
||||
lab2 = random()%3 + 1;
|
||||
for(i=0; i<lab2; i++) {
|
||||
labnum2[i] = random()%100;
|
||||
}
|
||||
} else {
|
||||
/* normal item */
|
||||
int common = random()%3;
|
||||
lab1 = random() %3 + 1;
|
||||
lab2 = random() %3 + 1;
|
||||
for(i=0; i<common; i++) {
|
||||
labnum1[i] = random()%100;
|
||||
labnum2[i] = labnum1[i];
|
||||
}
|
||||
labnum1[common] = random()%100;
|
||||
labnum2[common] = labnum1[common] + random()%20;
|
||||
for(i=common; i<lab1; i++)
|
||||
labnum1[i] = random()%100;
|
||||
for(i=common; i<lab2; i++)
|
||||
labnum2[i] = random()%100;
|
||||
}
|
||||
|
||||
/* construct first */
|
||||
p = buf1;
|
||||
for(i=0; i<lab1; i++) {
|
||||
snprintf(p, 256-(p-buf1), "\003%3.3d", labnum1[i]);
|
||||
p+=4;
|
||||
}
|
||||
snprintf(p, 256-(p-buf1), "%s", zname);
|
||||
|
||||
/* construct 2nd */
|
||||
p = buf2+2;
|
||||
for(i=0; i<lab2; i++) {
|
||||
snprintf(p, 256-(p-buf2)-3, "\003%3.3d", labnum2[i]);
|
||||
p+=4;
|
||||
}
|
||||
snprintf(p, 256-(p-buf2)-3, "%s", zname);
|
||||
buf2[0] = (char)(strlen(buf2+2)+1);
|
||||
buf2[1] = 0;
|
||||
|
||||
if(negverbose) {
|
||||
log_nametypeclass(0, "add from", (uint8_t*)buf1, 0, 0);
|
||||
log_nametypeclass(0, "add to ", (uint8_t*)buf2+2, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** add a random item */
|
||||
static void add_item(struct val_neg_cache* neg)
|
||||
{
|
||||
struct val_neg_zone* z;
|
||||
struct packed_rrset_data rd;
|
||||
struct ub_packed_rrset_key nsec;
|
||||
size_t rr_len;
|
||||
time_t rr_ttl;
|
||||
uint8_t* rr_data;
|
||||
char* zname = get_random_zone();
|
||||
char* from, *to;
|
||||
|
||||
lock_basic_lock(&neg->lock);
|
||||
if(negverbose)
|
||||
log_nametypeclass(0, "add to zone", (uint8_t*)zname, 0, 0);
|
||||
z = neg_find_zone(neg, (uint8_t*)zname, strlen(zname)+1,
|
||||
LDNS_RR_CLASS_IN);
|
||||
if(!z) {
|
||||
z = neg_create_zone(neg, (uint8_t*)zname, strlen(zname)+1,
|
||||
LDNS_RR_CLASS_IN);
|
||||
}
|
||||
unit_assert(z);
|
||||
val_neg_zone_take_inuse(z);
|
||||
|
||||
/* construct random NSEC item */
|
||||
get_random_data(&from, &to, zname);
|
||||
|
||||
/* create nsec and insert it */
|
||||
memset(&rd, 0, sizeof(rd));
|
||||
memset(&nsec, 0, sizeof(nsec));
|
||||
nsec.rk.dname = (uint8_t*)from;
|
||||
nsec.rk.dname_len = strlen(from)+1;
|
||||
nsec.rk.type = htons(LDNS_RR_TYPE_NSEC);
|
||||
nsec.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
|
||||
nsec.entry.data = &rd;
|
||||
rd.security = sec_status_secure;
|
||||
rd.count = 1;
|
||||
rd.rr_len = &rr_len;
|
||||
rr_len = 19;
|
||||
rd.rr_ttl = &rr_ttl;
|
||||
rr_ttl = 0;
|
||||
rd.rr_data = &rr_data;
|
||||
rr_data = (uint8_t*)to;
|
||||
|
||||
neg_insert_data(neg, z, &nsec);
|
||||
lock_basic_unlock(&neg->lock);
|
||||
}
|
||||
|
||||
/** remove a random item */
|
||||
static void remove_item(struct val_neg_cache* neg)
|
||||
{
|
||||
int n, i;
|
||||
struct val_neg_data* d;
|
||||
rbnode_t* walk;
|
||||
struct val_neg_zone* z;
|
||||
|
||||
lock_basic_lock(&neg->lock);
|
||||
if(neg->tree.count == 0) {
|
||||
lock_basic_unlock(&neg->lock);
|
||||
return; /* nothing to delete */
|
||||
}
|
||||
|
||||
/* pick a random zone */
|
||||
walk = rbtree_first(&neg->tree); /* first highest parent, big count */
|
||||
z = (struct val_neg_zone*)walk;
|
||||
n = random() % (int)(z->count);
|
||||
if(negverbose)
|
||||
printf("neg stress delete zone %d\n", n);
|
||||
i=0;
|
||||
walk = rbtree_first(&neg->tree);
|
||||
z = (struct val_neg_zone*)walk;
|
||||
while(i!=n+1 && walk && walk != RBTREE_NULL && !z->in_use) {
|
||||
walk = rbtree_next(walk);
|
||||
z = (struct val_neg_zone*)walk;
|
||||
if(z->in_use)
|
||||
i++;
|
||||
}
|
||||
if(!walk || walk == RBTREE_NULL) {
|
||||
lock_basic_unlock(&neg->lock);
|
||||
return;
|
||||
}
|
||||
if(!z->in_use) {
|
||||
lock_basic_unlock(&neg->lock);
|
||||
return;
|
||||
}
|
||||
if(negverbose)
|
||||
log_nametypeclass(0, "delete zone", z->name, 0, 0);
|
||||
|
||||
/* pick a random nsec item. - that is in use */
|
||||
walk = rbtree_first(&z->tree); /* first is highest parent */
|
||||
d = (struct val_neg_data*)walk;
|
||||
n = random() % (int)(d->count);
|
||||
if(negverbose)
|
||||
printf("neg stress delete item %d\n", n);
|
||||
i=0;
|
||||
walk = rbtree_first(&z->tree);
|
||||
d = (struct val_neg_data*)walk;
|
||||
while(i!=n+1 && walk && walk != RBTREE_NULL && !d->in_use) {
|
||||
walk = rbtree_next(walk);
|
||||
d = (struct val_neg_data*)walk;
|
||||
if(d->in_use)
|
||||
i++;
|
||||
}
|
||||
if(!walk || walk == RBTREE_NULL) {
|
||||
lock_basic_unlock(&neg->lock);
|
||||
return;
|
||||
}
|
||||
if(d->in_use) {
|
||||
if(negverbose)
|
||||
log_nametypeclass(0, "neg delete item:", d->name, 0, 0);
|
||||
neg_delete_data(neg, d);
|
||||
}
|
||||
lock_basic_unlock(&neg->lock);
|
||||
}
|
||||
|
||||
/** sum up the zone trees */
|
||||
static size_t sumtrees_all(struct val_neg_cache* neg)
|
||||
{
|
||||
size_t res = 0;
|
||||
struct val_neg_zone* z;
|
||||
RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
|
||||
res += z->tree.count;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** sum up the zone trees, in_use only */
|
||||
static size_t sumtrees_inuse(struct val_neg_cache* neg)
|
||||
{
|
||||
size_t res = 0;
|
||||
struct val_neg_zone* z;
|
||||
struct val_neg_data* d;
|
||||
RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
|
||||
/* get count of highest parent for num in use */
|
||||
d = (struct val_neg_data*)rbtree_first(&z->tree);
|
||||
if(d && (rbnode_t*)d!=RBTREE_NULL)
|
||||
res += d->count;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** check if lru is still valid */
|
||||
static void check_lru(struct val_neg_cache* neg)
|
||||
{
|
||||
struct val_neg_data* p, *np;
|
||||
size_t num = 0;
|
||||
size_t inuse;
|
||||
p = neg->first;
|
||||
while(p) {
|
||||
if(!p->prev) {
|
||||
unit_assert(neg->first == p);
|
||||
}
|
||||
np = p->next;
|
||||
if(np) {
|
||||
unit_assert(np->prev == p);
|
||||
} else {
|
||||
unit_assert(neg->last == p);
|
||||
}
|
||||
num++;
|
||||
p = np;
|
||||
}
|
||||
inuse = sumtrees_inuse(neg);
|
||||
if(negverbose)
|
||||
printf("num lru %d, inuse %d, all %d\n",
|
||||
(int)num, (int)sumtrees_inuse(neg),
|
||||
(int)sumtrees_all(neg));
|
||||
unit_assert( num == inuse);
|
||||
unit_assert( inuse <= sumtrees_all(neg));
|
||||
}
|
||||
|
||||
/** sum up number of items inuse in subtree */
|
||||
static int sum_subtree_inuse(struct val_neg_zone* zone,
|
||||
struct val_neg_data* data)
|
||||
{
|
||||
struct val_neg_data* d;
|
||||
int num = 0;
|
||||
RBTREE_FOR(d, struct val_neg_data*, &zone->tree) {
|
||||
if(dname_subdomain_c(d->name, data->name)) {
|
||||
if(d->in_use)
|
||||
num++;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/** sum up number of items inuse in subtree */
|
||||
static int sum_zone_subtree_inuse(struct val_neg_cache* neg,
|
||||
struct val_neg_zone* zone)
|
||||
{
|
||||
struct val_neg_zone* z;
|
||||
int num = 0;
|
||||
RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
|
||||
if(dname_subdomain_c(z->name, zone->name)) {
|
||||
if(z->in_use)
|
||||
num++;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/** check point in data tree */
|
||||
static void check_data(struct val_neg_zone* zone, struct val_neg_data* data)
|
||||
{
|
||||
unit_assert(data->count > 0);
|
||||
if(data->parent) {
|
||||
unit_assert(data->parent->count >= data->count);
|
||||
if(data->parent->in_use) {
|
||||
unit_assert(data->parent->count > data->count);
|
||||
}
|
||||
unit_assert(data->parent->labs == data->labs-1);
|
||||
/* and parent must be one label shorter */
|
||||
unit_assert(data->name[0] == (data->len-data->parent->len-1));
|
||||
unit_assert(query_dname_compare(data->name + data->name[0]+1,
|
||||
data->parent->name) == 0);
|
||||
} else {
|
||||
/* must be apex */
|
||||
unit_assert(dname_is_root(data->name));
|
||||
}
|
||||
/* tree property: */
|
||||
unit_assert(data->count == sum_subtree_inuse(zone, data));
|
||||
}
|
||||
|
||||
/** check if tree of data in zone is valid */
|
||||
static void checkzonetree(struct val_neg_zone* zone)
|
||||
{
|
||||
struct val_neg_data* d;
|
||||
|
||||
/* check all data in tree */
|
||||
RBTREE_FOR(d, struct val_neg_data*, &zone->tree) {
|
||||
check_data(zone, d);
|
||||
}
|
||||
}
|
||||
|
||||
/** check if negative cache is still valid */
|
||||
static void check_zone_invariants(struct val_neg_cache* neg,
|
||||
struct val_neg_zone* zone)
|
||||
{
|
||||
unit_assert(zone->nsec3_hash == 0);
|
||||
unit_assert(zone->tree.cmp == &val_neg_data_compare);
|
||||
unit_assert(zone->count != 0);
|
||||
|
||||
if(zone->tree.count == 0)
|
||||
unit_assert(!zone->in_use);
|
||||
else {
|
||||
if(!zone->in_use) {
|
||||
/* details on error */
|
||||
log_nametypeclass(0, "zone", zone->name, 0, 0);
|
||||
log_err("inuse %d count=%d tree.count=%d",
|
||||
zone->in_use, zone->count,
|
||||
(int)zone->tree.count);
|
||||
if(negverbose)
|
||||
print_neg_cache(neg);
|
||||
}
|
||||
unit_assert(zone->in_use);
|
||||
}
|
||||
|
||||
if(zone->parent) {
|
||||
unit_assert(zone->parent->count >= zone->count);
|
||||
if(zone->parent->in_use) {
|
||||
unit_assert(zone->parent->count > zone->count);
|
||||
}
|
||||
unit_assert(zone->parent->labs == zone->labs-1);
|
||||
/* and parent must be one label shorter */
|
||||
unit_assert(zone->name[0] == (zone->len-zone->parent->len-1));
|
||||
unit_assert(query_dname_compare(zone->name + zone->name[0]+1,
|
||||
zone->parent->name) == 0);
|
||||
} else {
|
||||
/* must be apex */
|
||||
unit_assert(dname_is_root(zone->name));
|
||||
}
|
||||
/* tree property: */
|
||||
unit_assert(zone->count == sum_zone_subtree_inuse(neg, zone));
|
||||
|
||||
/* check structure of zone data tree */
|
||||
checkzonetree(zone);
|
||||
}
|
||||
|
||||
/** check if negative cache is still valid */
|
||||
static void check_neg_invariants(struct val_neg_cache* neg)
|
||||
{
|
||||
struct val_neg_zone* z;
|
||||
/* check structure of LRU list */
|
||||
lock_basic_lock(&neg->lock);
|
||||
check_lru(neg);
|
||||
unit_assert(neg->max == 1024*1024);
|
||||
unit_assert(neg->nsec3_max_iter == 1500);
|
||||
unit_assert(neg->tree.cmp == &val_neg_zone_compare);
|
||||
|
||||
if(neg->tree.count == 0) {
|
||||
/* empty */
|
||||
unit_assert(neg->tree.count == 0);
|
||||
unit_assert(neg->first == NULL);
|
||||
unit_assert(neg->last == NULL);
|
||||
unit_assert(neg->use == 0);
|
||||
lock_basic_unlock(&neg->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
unit_assert(neg->first != NULL);
|
||||
unit_assert(neg->last != NULL);
|
||||
|
||||
RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
|
||||
check_zone_invariants(neg, z);
|
||||
}
|
||||
lock_basic_unlock(&neg->lock);
|
||||
}
|
||||
|
||||
/** perform stress test on insert and delete in neg cache */
|
||||
static void stress_test(struct val_neg_cache* neg)
|
||||
{
|
||||
int i;
|
||||
if(negverbose)
|
||||
printf("negcache test\n");
|
||||
for(i=0; i<100; i++) {
|
||||
if(random() % 10 < 8)
|
||||
add_item(neg);
|
||||
else remove_item(neg);
|
||||
check_neg_invariants(neg);
|
||||
}
|
||||
/* empty it */
|
||||
if(negverbose)
|
||||
printf("neg stress empty\n");
|
||||
while(neg->first) {
|
||||
remove_item(neg);
|
||||
check_neg_invariants(neg);
|
||||
}
|
||||
if(negverbose)
|
||||
printf("neg stress emptied\n");
|
||||
unit_assert(neg->first == NULL);
|
||||
/* insert again */
|
||||
for(i=0; i<100; i++) {
|
||||
if(random() % 10 < 8)
|
||||
add_item(neg);
|
||||
else remove_item(neg);
|
||||
check_neg_invariants(neg);
|
||||
}
|
||||
}
|
||||
|
||||
void neg_test(void)
|
||||
{
|
||||
struct val_neg_cache* neg;
|
||||
srandom(48);
|
||||
unit_show_feature("negative cache");
|
||||
|
||||
/* create with defaults */
|
||||
neg = val_neg_create(NULL, 1500);
|
||||
unit_assert(neg);
|
||||
|
||||
stress_test(neg);
|
||||
|
||||
neg_cache_delete(neg);
|
||||
}
|
||||
244
external/unbound/testcode/unitregional.c
vendored
Normal file
244
external/unbound/testcode/unitregional.c
vendored
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* testcode/unitregional.c - unit test for regional allocator.
|
||||
*
|
||||
* Copyright (c) 2010, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* \file
|
||||
* Tests the regional special purpose allocator.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "testcode/unitmain.h"
|
||||
#include "util/log.h"
|
||||
#include "util/regional.h"
|
||||
|
||||
/** test regional corner cases, zero, one, end of structure */
|
||||
static void
|
||||
corner_cases(struct regional* r)
|
||||
{
|
||||
size_t s; /* shadow count of allocated memory */
|
||||
void* a;
|
||||
size_t minsize = sizeof(uint64_t);
|
||||
size_t mysize;
|
||||
char* str;
|
||||
unit_assert(r);
|
||||
/* alloc cases:
|
||||
* 0, 1, 2.
|
||||
* smaller than LARGE_OBJECT_SIZE.
|
||||
* smaller but does not fit in remainder in regional.
|
||||
* smaller but exactly fits in remainder of regional.
|
||||
* size is remainder of regional - 8.
|
||||
* size is remainder of regional + 8.
|
||||
* larger than LARGE_OBJECT_SIZE.
|
||||
*/
|
||||
s = sizeof(struct regional);
|
||||
unit_assert((s % minsize) == 0);
|
||||
unit_assert(r->available == r->first_size - s);
|
||||
unit_assert(r->large_list == NULL);
|
||||
unit_assert(r->next == NULL);
|
||||
|
||||
/* Note an alloc of 0 gets a pointer to current last
|
||||
* position (where you should then use 0 bytes) */
|
||||
a = regional_alloc(r, 0);
|
||||
unit_assert(a);
|
||||
s+=0;
|
||||
unit_assert(r->available == r->first_size - s);
|
||||
|
||||
a = regional_alloc(r, 1);
|
||||
unit_assert(a);
|
||||
memset(a, 0x42, 1);
|
||||
s+=minsize;
|
||||
unit_assert(r->available == r->first_size - s);
|
||||
|
||||
a = regional_alloc(r, 2);
|
||||
unit_assert(a);
|
||||
memset(a, 0x42, 2);
|
||||
s+=minsize;
|
||||
unit_assert(r->available == r->first_size - s);
|
||||
|
||||
a = regional_alloc(r, 128);
|
||||
unit_assert(a);
|
||||
memset(a, 0x42, 128);
|
||||
s+=128;
|
||||
unit_assert(r->available == r->first_size - s);
|
||||
|
||||
unit_assert(r->large_list == NULL);
|
||||
a = regional_alloc(r, 10240);
|
||||
unit_assert(a);
|
||||
unit_assert(r->large_list != NULL);
|
||||
memset(a, 0x42, 10240);
|
||||
/* s does not change */
|
||||
unit_assert(r->available == r->first_size - s);
|
||||
unit_assert(r->total_large == 10240+minsize);
|
||||
|
||||
/* go towards the end of the current chunk */
|
||||
while(r->available > 1024) {
|
||||
a = regional_alloc(r, 1024);
|
||||
unit_assert(a);
|
||||
memset(a, 0x42, 1024);
|
||||
s += 1024;
|
||||
unit_assert(r->available == r->first_size - s);
|
||||
}
|
||||
|
||||
unit_assert(r->next == NULL);
|
||||
mysize = 1280; /* does not fit in current chunk */
|
||||
a = regional_alloc(r, mysize);
|
||||
memset(a, 0x42, mysize);
|
||||
unit_assert(r->next != NULL);
|
||||
unit_assert(a);
|
||||
|
||||
/* go towards the end of the current chunk */
|
||||
while(r->available > 864) {
|
||||
a = regional_alloc(r, 864);
|
||||
unit_assert(a);
|
||||
memset(a, 0x42, 864);
|
||||
s += 864;
|
||||
}
|
||||
|
||||
mysize = r->available; /* exactly fits */
|
||||
a = regional_alloc(r, mysize);
|
||||
memset(a, 0x42, mysize);
|
||||
unit_assert(a);
|
||||
unit_assert(r->available == 0); /* implementation does not go ahead*/
|
||||
|
||||
a = regional_alloc(r, 8192); /* another large allocation */
|
||||
unit_assert(a);
|
||||
memset(a, 0x42, 8192);
|
||||
unit_assert(r->available == 0);
|
||||
unit_assert(r->total_large == 10240 + 8192 + 2*minsize);
|
||||
|
||||
a = regional_alloc(r, 32); /* make new chunk */
|
||||
unit_assert(a);
|
||||
memset(a, 0x42, 32);
|
||||
unit_assert(r->available > 0);
|
||||
unit_assert(r->total_large == 10240 + 8192 + 2*minsize);
|
||||
|
||||
/* go towards the end of the current chunk */
|
||||
while(r->available > 1320) {
|
||||
a = regional_alloc(r, 1320);
|
||||
unit_assert(a);
|
||||
memset(a, 0x42, 1320);
|
||||
s += 1320;
|
||||
}
|
||||
|
||||
mysize = r->available + 8; /* exact + 8 ; does not fit */
|
||||
a = regional_alloc(r, mysize);
|
||||
memset(a, 0x42, mysize);
|
||||
unit_assert(a);
|
||||
unit_assert(r->available > 0); /* new chunk */
|
||||
|
||||
/* go towards the end of the current chunk */
|
||||
while(r->available > 1480) {
|
||||
a = regional_alloc(r, 1480);
|
||||
unit_assert(a);
|
||||
memset(a, 0x42, 1480);
|
||||
s += 1480;
|
||||
}
|
||||
|
||||
mysize = r->available - 8; /* exact - 8 ; fits. */
|
||||
a = regional_alloc(r, mysize);
|
||||
memset(a, 0x42, mysize);
|
||||
unit_assert(a);
|
||||
unit_assert(r->available == 8);
|
||||
|
||||
/* test if really copied over */
|
||||
str = "test12345";
|
||||
a = regional_alloc_init(r, str, 8);
|
||||
unit_assert(a);
|
||||
unit_assert(memcmp(a, str, 8) == 0);
|
||||
|
||||
/* test if really zeroed */
|
||||
a = regional_alloc_zero(r, 32);
|
||||
str="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
||||
unit_assert(a);
|
||||
unit_assert(memcmp(a, str, 32) == 0);
|
||||
|
||||
/* test if copied over (and null byte) */
|
||||
str = "an interesting string";
|
||||
a = regional_strdup(r, str);
|
||||
unit_assert(a);
|
||||
unit_assert(memcmp(a, str, strlen(str)+1) == 0);
|
||||
|
||||
regional_free_all(r);
|
||||
}
|
||||
|
||||
/** test specific cases */
|
||||
static void
|
||||
specific_cases(void)
|
||||
{
|
||||
struct regional* r = regional_create();
|
||||
corner_cases(r);
|
||||
regional_destroy(r);
|
||||
r = regional_create_custom(2048); /* a small regional */
|
||||
unit_assert(r->first_size == 2048);
|
||||
unit_assert(regional_get_mem(r) == 2048);
|
||||
corner_cases(r);
|
||||
unit_assert(regional_get_mem(r) == 2048);
|
||||
regional_destroy(r);
|
||||
}
|
||||
|
||||
/** put random stuff in a region and free it */
|
||||
static void
|
||||
burden_test(size_t max)
|
||||
{
|
||||
size_t get;
|
||||
void* a;
|
||||
int i;
|
||||
struct regional* r = regional_create_custom(2048);
|
||||
for(i=0; i<1000; i++) {
|
||||
get = random() % max;
|
||||
a = regional_alloc(r, get);
|
||||
unit_assert(a);
|
||||
memset(a, 0x54, get);
|
||||
}
|
||||
regional_free_all(r);
|
||||
regional_destroy(r);
|
||||
}
|
||||
|
||||
/** randomly allocate stuff */
|
||||
static void
|
||||
random_burden(void)
|
||||
{
|
||||
size_t max_alloc = 2048 + 128; /* small chance of LARGE */
|
||||
int i;
|
||||
for(i=0; i<100; i++)
|
||||
burden_test(max_alloc);
|
||||
}
|
||||
|
||||
void regional_test(void)
|
||||
{
|
||||
unit_show_feature("regional");
|
||||
specific_cases();
|
||||
random_burden();
|
||||
}
|
||||
376
external/unbound/testcode/unitslabhash.c
vendored
Normal file
376
external/unbound/testcode/unitslabhash.c
vendored
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
* testcode/unitslabhash.c - unit test for slabhash table.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* \file
|
||||
* Tests the locking LRU keeping hash table implementation.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "testcode/unitmain.h"
|
||||
#include "util/log.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
|
||||
/** use this type for the slabhash test key */
|
||||
typedef struct slabhash_testkey testkey_t;
|
||||
/** use this type for the slabhash test data */
|
||||
typedef struct slabhash_testdata testdata_t;
|
||||
|
||||
/** delete key */
|
||||
static void delkey(struct slabhash_testkey* k) {
|
||||
lock_rw_destroy(&k->entry.lock); free(k);}
|
||||
|
||||
/** hash func, very bad to improve collisions, both high and low bits */
|
||||
static hashvalue_t myhash(int id) {
|
||||
hashvalue_t h = (hashvalue_t)id & 0x0f;
|
||||
h |= (h << 28);
|
||||
return h;
|
||||
}
|
||||
|
||||
/** allocate new key, fill in hash */
|
||||
static testkey_t* newkey(int id) {
|
||||
testkey_t* k = (testkey_t*)calloc(1, sizeof(testkey_t));
|
||||
if(!k) fatal_exit("out of memory");
|
||||
k->id = id;
|
||||
k->entry.hash = myhash(id);
|
||||
k->entry.key = k;
|
||||
lock_rw_init(&k->entry.lock);
|
||||
return k;
|
||||
}
|
||||
/** new data el */
|
||||
static testdata_t* newdata(int val) {
|
||||
testdata_t* d = (testdata_t*)calloc(1,
|
||||
sizeof(testdata_t));
|
||||
if(!d) fatal_exit("out of memory");
|
||||
d->data = val;
|
||||
return d;
|
||||
}
|
||||
|
||||
/** test hashtable using short sequence */
|
||||
static void
|
||||
test_short_table(struct slabhash* table)
|
||||
{
|
||||
testkey_t* k = newkey(12);
|
||||
testkey_t* k2 = newkey(14);
|
||||
testdata_t* d = newdata(128);
|
||||
testdata_t* d2 = newdata(129);
|
||||
|
||||
k->entry.data = d;
|
||||
k2->entry.data = d2;
|
||||
|
||||
slabhash_insert(table, myhash(12), &k->entry, d, NULL);
|
||||
slabhash_insert(table, myhash(14), &k2->entry, d2, NULL);
|
||||
|
||||
unit_assert( slabhash_lookup(table, myhash(12), k, 0) == &k->entry);
|
||||
lock_rw_unlock( &k->entry.lock );
|
||||
unit_assert( slabhash_lookup(table, myhash(14), k2, 0) == &k2->entry);
|
||||
lock_rw_unlock( &k2->entry.lock );
|
||||
slabhash_remove(table, myhash(12), k);
|
||||
slabhash_remove(table, myhash(14), k2);
|
||||
}
|
||||
|
||||
/** number of hash test max */
|
||||
#define HASHTESTMAX 32
|
||||
|
||||
/** test adding a random element */
|
||||
static void
|
||||
testadd(struct slabhash* table, testdata_t* ref[])
|
||||
{
|
||||
int numtoadd = random() % HASHTESTMAX;
|
||||
testdata_t* data = newdata(numtoadd);
|
||||
testkey_t* key = newkey(numtoadd);
|
||||
key->entry.data = data;
|
||||
slabhash_insert(table, myhash(numtoadd), &key->entry, data, NULL);
|
||||
ref[numtoadd] = data;
|
||||
}
|
||||
|
||||
/** test adding a random element */
|
||||
static void
|
||||
testremove(struct slabhash* table, testdata_t* ref[])
|
||||
{
|
||||
int num = random() % HASHTESTMAX;
|
||||
testkey_t* key = newkey(num);
|
||||
slabhash_remove(table, myhash(num), key);
|
||||
ref[num] = NULL;
|
||||
delkey(key);
|
||||
}
|
||||
|
||||
/** test adding a random element */
|
||||
static void
|
||||
testlookup(struct slabhash* table, testdata_t* ref[])
|
||||
{
|
||||
int num = random() % HASHTESTMAX;
|
||||
testkey_t* key = newkey(num);
|
||||
struct lruhash_entry* en = slabhash_lookup(table, myhash(num), key, 0);
|
||||
testdata_t* data = en? (testdata_t*)en->data : NULL;
|
||||
if(en) {
|
||||
unit_assert(en->key);
|
||||
unit_assert(en->data);
|
||||
}
|
||||
if(0) log_info("lookup %d got %d, expect %d", num, en? data->data :-1,
|
||||
ref[num]? ref[num]->data : -1);
|
||||
unit_assert( data == ref[num] );
|
||||
if(en) { lock_rw_unlock(&en->lock); }
|
||||
delkey(key);
|
||||
}
|
||||
|
||||
/** check integrity of hash table */
|
||||
static void
|
||||
check_lru_table(struct lruhash* table)
|
||||
{
|
||||
struct lruhash_entry* p;
|
||||
size_t c = 0;
|
||||
lock_quick_lock(&table->lock);
|
||||
unit_assert( table->num <= table->size);
|
||||
unit_assert( table->size_mask == (int)table->size-1 );
|
||||
unit_assert( (table->lru_start && table->lru_end) ||
|
||||
(!table->lru_start && !table->lru_end) );
|
||||
unit_assert( table->space_used <= table->space_max );
|
||||
/* check lru list integrity */
|
||||
if(table->lru_start)
|
||||
unit_assert(table->lru_start->lru_prev == NULL);
|
||||
if(table->lru_end)
|
||||
unit_assert(table->lru_end->lru_next == NULL);
|
||||
p = table->lru_start;
|
||||
while(p) {
|
||||
if(p->lru_prev) {
|
||||
unit_assert(p->lru_prev->lru_next == p);
|
||||
}
|
||||
if(p->lru_next) {
|
||||
unit_assert(p->lru_next->lru_prev == p);
|
||||
}
|
||||
c++;
|
||||
p = p->lru_next;
|
||||
}
|
||||
unit_assert(c == table->num);
|
||||
|
||||
/* this assertion is specific to the unit test */
|
||||
unit_assert( table->space_used ==
|
||||
table->num * test_slabhash_sizefunc(NULL, NULL) );
|
||||
lock_quick_unlock(&table->lock);
|
||||
}
|
||||
|
||||
/** check integrity of hash table */
|
||||
static void
|
||||
check_table(struct slabhash* table)
|
||||
{
|
||||
size_t i;
|
||||
for(i=0; i<table->size; i++)
|
||||
check_lru_table(table->array[i]);
|
||||
}
|
||||
|
||||
/** test adding a random element (unlimited range) */
|
||||
static void
|
||||
testadd_unlim(struct slabhash* table, testdata_t** ref)
|
||||
{
|
||||
int numtoadd = random() % (HASHTESTMAX * 10);
|
||||
testdata_t* data = newdata(numtoadd);
|
||||
testkey_t* key = newkey(numtoadd);
|
||||
key->entry.data = data;
|
||||
slabhash_insert(table, myhash(numtoadd), &key->entry, data, NULL);
|
||||
if(ref)
|
||||
ref[numtoadd] = data;
|
||||
}
|
||||
|
||||
/** test adding a random element (unlimited range) */
|
||||
static void
|
||||
testremove_unlim(struct slabhash* table, testdata_t** ref)
|
||||
{
|
||||
int num = random() % (HASHTESTMAX*10);
|
||||
testkey_t* key = newkey(num);
|
||||
slabhash_remove(table, myhash(num), key);
|
||||
if(ref)
|
||||
ref[num] = NULL;
|
||||
delkey(key);
|
||||
}
|
||||
|
||||
/** test adding a random element (unlimited range) */
|
||||
static void
|
||||
testlookup_unlim(struct slabhash* table, testdata_t** ref)
|
||||
{
|
||||
int num = random() % (HASHTESTMAX*10);
|
||||
testkey_t* key = newkey(num);
|
||||
struct lruhash_entry* en = slabhash_lookup(table, myhash(num), key, 0);
|
||||
testdata_t* data = en? (testdata_t*)en->data : NULL;
|
||||
if(en) {
|
||||
unit_assert(en->key);
|
||||
unit_assert(en->data);
|
||||
}
|
||||
if(0 && ref) log_info("lookup unlim %d got %d, expect %d", num, en ?
|
||||
data->data :-1, ref[num] ? ref[num]->data : -1);
|
||||
if(data && ref) {
|
||||
/* its okay for !data, it fell off the lru */
|
||||
unit_assert( data == ref[num] );
|
||||
}
|
||||
if(en) { lock_rw_unlock(&en->lock); }
|
||||
delkey(key);
|
||||
}
|
||||
|
||||
/** test with long sequence of adds, removes and updates, and lookups */
|
||||
static void
|
||||
test_long_table(struct slabhash* table)
|
||||
{
|
||||
/* assuming it all fits in the hastable, this check will work */
|
||||
testdata_t* ref[HASHTESTMAX * 100];
|
||||
size_t i;
|
||||
memset(ref, 0, sizeof(ref));
|
||||
/* test assumption */
|
||||
if(0) slabhash_status(table, "unit test", 1);
|
||||
srandom(48);
|
||||
for(i=0; i<1000; i++) {
|
||||
/* what to do? */
|
||||
if(i == 500) {
|
||||
slabhash_clear(table);
|
||||
memset(ref, 0, sizeof(ref));
|
||||
continue;
|
||||
}
|
||||
switch(random() % 4) {
|
||||
case 0:
|
||||
case 3:
|
||||
testadd(table, ref);
|
||||
break;
|
||||
case 1:
|
||||
testremove(table, ref);
|
||||
break;
|
||||
case 2:
|
||||
testlookup(table, ref);
|
||||
break;
|
||||
default:
|
||||
unit_assert(0);
|
||||
}
|
||||
if(0) slabhash_status(table, "unit test", 1);
|
||||
check_table(table);
|
||||
}
|
||||
|
||||
/* test more, but 'ref' assumption does not hold anymore */
|
||||
for(i=0; i<1000; i++) {
|
||||
/* what to do? */
|
||||
switch(random() % 4) {
|
||||
case 0:
|
||||
case 3:
|
||||
testadd_unlim(table, ref);
|
||||
break;
|
||||
case 1:
|
||||
testremove_unlim(table, ref);
|
||||
break;
|
||||
case 2:
|
||||
testlookup_unlim(table, ref);
|
||||
break;
|
||||
default:
|
||||
unit_assert(0);
|
||||
}
|
||||
if(0) slabhash_status(table, "unlim", 1);
|
||||
check_table(table);
|
||||
}
|
||||
}
|
||||
|
||||
/** structure to threaded test the lru hash table */
|
||||
struct slab_test_thr {
|
||||
/** thread num, first entry. */
|
||||
int num;
|
||||
/** id */
|
||||
ub_thread_t id;
|
||||
/** hash table */
|
||||
struct slabhash* table;
|
||||
};
|
||||
|
||||
/** main routine for threaded hash table test */
|
||||
static void*
|
||||
test_thr_main(void* arg)
|
||||
{
|
||||
struct slab_test_thr* t = (struct slab_test_thr*)arg;
|
||||
int i;
|
||||
log_thread_set(&t->num);
|
||||
for(i=0; i<1000; i++) {
|
||||
switch(random() % 4) {
|
||||
case 0:
|
||||
case 3:
|
||||
testadd_unlim(t->table, NULL);
|
||||
break;
|
||||
case 1:
|
||||
testremove_unlim(t->table, NULL);
|
||||
break;
|
||||
case 2:
|
||||
testlookup_unlim(t->table, NULL);
|
||||
break;
|
||||
default:
|
||||
unit_assert(0);
|
||||
}
|
||||
if(0) slabhash_status(t->table, "hashtest", 1);
|
||||
if(i % 100 == 0) /* because of locking, not all the time */
|
||||
check_table(t->table);
|
||||
}
|
||||
check_table(t->table);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** test hash table access by multiple threads */
|
||||
static void
|
||||
test_threaded_table(struct slabhash* table)
|
||||
{
|
||||
int numth = 10;
|
||||
struct slab_test_thr t[100];
|
||||
int i;
|
||||
|
||||
for(i=1; i<numth; i++) {
|
||||
t[i].num = i;
|
||||
t[i].table = table;
|
||||
ub_thread_create(&t[i].id, test_thr_main, &t[i]);
|
||||
}
|
||||
|
||||
for(i=1; i<numth; i++) {
|
||||
ub_thread_join(t[i].id);
|
||||
}
|
||||
if(0) slabhash_status(table, "hashtest", 1);
|
||||
}
|
||||
|
||||
void slabhash_test(void)
|
||||
{
|
||||
/* start very very small array, so it can do lots of table_grow() */
|
||||
/* also small in size so that reclaim has to be done quickly. */
|
||||
struct slabhash* table;
|
||||
unit_show_feature("slabhash");
|
||||
table = slabhash_create(4, 2, 10400,
|
||||
test_slabhash_sizefunc, test_slabhash_compfunc,
|
||||
test_slabhash_delkey, test_slabhash_deldata, NULL);
|
||||
test_short_table(table);
|
||||
test_long_table(table);
|
||||
slabhash_delete(table);
|
||||
table = slabhash_create(4, 2, 10400,
|
||||
test_slabhash_sizefunc, test_slabhash_compfunc,
|
||||
test_slabhash_delkey, test_slabhash_deldata, NULL);
|
||||
test_threaded_table(table);
|
||||
slabhash_delete(table);
|
||||
}
|
||||
533
external/unbound/testcode/unitverify.c
vendored
Normal file
533
external/unbound/testcode/unitverify.c
vendored
Normal file
|
|
@ -0,0 +1,533 @@
|
|||
/*
|
||||
* testcode/unitverify.c - unit test for signature verification routines.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* \file
|
||||
* Calls verification unit tests. Exits with code 1 on a failure.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "testcode/unitmain.h"
|
||||
#include "validator/val_sigcrypt.h"
|
||||
#include "validator/val_secalgo.h"
|
||||
#include "validator/val_nsec.h"
|
||||
#include "validator/val_nsec3.h"
|
||||
#include "validator/validator.h"
|
||||
#include "testcode/testpkts.h"
|
||||
#include "util/data/msgreply.h"
|
||||
#include "util/data/msgparse.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/alloc.h"
|
||||
#include "util/rbtree.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/module.h"
|
||||
#include "util/config_file.h"
|
||||
#include "ldns/sbuffer.h"
|
||||
#include "ldns/keyraw.h"
|
||||
#include "ldns/str2wire.h"
|
||||
#include "ldns/wire2str.h"
|
||||
|
||||
/** verbose signature test */
|
||||
static int vsig = 0;
|
||||
|
||||
/** entry to packet buffer with wireformat */
|
||||
static void
|
||||
entry_to_buf(struct entry* e, sldns_buffer* pkt)
|
||||
{
|
||||
unit_assert(e->reply_list);
|
||||
if(e->reply_list->reply_from_hex) {
|
||||
sldns_buffer_copy(pkt, e->reply_list->reply_from_hex);
|
||||
} else {
|
||||
sldns_buffer_clear(pkt);
|
||||
sldns_buffer_write(pkt, e->reply_list->reply_pkt,
|
||||
e->reply_list->reply_len);
|
||||
sldns_buffer_flip(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
/** entry to reply info conversion */
|
||||
static void
|
||||
entry_to_repinfo(struct entry* e, struct alloc_cache* alloc,
|
||||
struct regional* region, sldns_buffer* pkt, struct query_info* qi,
|
||||
struct reply_info** rep)
|
||||
{
|
||||
int ret;
|
||||
struct edns_data edns;
|
||||
entry_to_buf(e, pkt);
|
||||
/* lock alloc lock to please lock checking software.
|
||||
* alloc_special_obtain assumes it is talking to a ub-alloc,
|
||||
* and does not need to perform locking. Here the alloc is
|
||||
* the only one, so we lock it here */
|
||||
lock_quick_lock(&alloc->lock);
|
||||
ret = reply_info_parse(pkt, alloc, qi, rep, region, &edns);
|
||||
lock_quick_unlock(&alloc->lock);
|
||||
if(ret != 0) {
|
||||
char rcode[16];
|
||||
sldns_wire2str_rcode_buf(ret, rcode, sizeof(rcode));
|
||||
printf("parse code %d: %s\n", ret, rcode);
|
||||
unit_assert(ret != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** extract DNSKEY rrset from answer and convert it */
|
||||
static struct ub_packed_rrset_key*
|
||||
extract_keys(struct entry* e, struct alloc_cache* alloc,
|
||||
struct regional* region, sldns_buffer* pkt)
|
||||
{
|
||||
struct ub_packed_rrset_key* dnskey = NULL;
|
||||
struct query_info qinfo;
|
||||
struct reply_info* rep = NULL;
|
||||
size_t i;
|
||||
|
||||
entry_to_repinfo(e, alloc, region, pkt, &qinfo, &rep);
|
||||
for(i=0; i<rep->an_numrrsets; i++) {
|
||||
if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_DNSKEY) {
|
||||
dnskey = rep->rrsets[i];
|
||||
rep->rrsets[i] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unit_assert(dnskey);
|
||||
|
||||
reply_info_parsedelete(rep, alloc);
|
||||
query_info_clear(&qinfo);
|
||||
return dnskey;
|
||||
}
|
||||
|
||||
/** return true if answer should be bogus */
|
||||
static int
|
||||
should_be_bogus(struct ub_packed_rrset_key* rrset, struct query_info* qinfo)
|
||||
{
|
||||
struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
|
||||
entry.data;
|
||||
if(d->rrsig_count == 0)
|
||||
return 1;
|
||||
/* name 'bogus' as first label signals bogus */
|
||||
if(rrset->rk.dname_len > 6 && memcmp(rrset->rk.dname+1, "bogus", 5)==0)
|
||||
return 1;
|
||||
if(qinfo->qname_len > 6 && memcmp(qinfo->qname+1, "bogus", 5)==0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** return number of rrs in an rrset */
|
||||
static size_t
|
||||
rrset_get_count(struct ub_packed_rrset_key* rrset)
|
||||
{
|
||||
struct packed_rrset_data* d = (struct packed_rrset_data*)
|
||||
rrset->entry.data;
|
||||
if(!d) return 0;
|
||||
return d->count;
|
||||
}
|
||||
|
||||
/** setup sig alg list from dnskey */
|
||||
static void
|
||||
setup_sigalg(struct ub_packed_rrset_key* dnskey, uint8_t* sigalg)
|
||||
{
|
||||
uint8_t a[ALGO_NEEDS_MAX];
|
||||
size_t i, n = 0;
|
||||
memset(a, 0, sizeof(a));
|
||||
for(i=0; i<rrset_get_count(dnskey); i++) {
|
||||
uint8_t algo = (uint8_t)dnskey_get_algo(dnskey, i);
|
||||
if(a[algo] == 0) {
|
||||
a[algo] = 1;
|
||||
sigalg[n++] = algo;
|
||||
}
|
||||
}
|
||||
sigalg[n] = 0;
|
||||
}
|
||||
|
||||
/** verify and test one rrset against the key rrset */
|
||||
static void
|
||||
verifytest_rrset(struct module_env* env, struct val_env* ve,
|
||||
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
|
||||
struct query_info* qinfo)
|
||||
{
|
||||
enum sec_status sec;
|
||||
char* reason = NULL;
|
||||
uint8_t sigalg[ALGO_NEEDS_MAX+1];
|
||||
if(vsig) {
|
||||
log_nametypeclass(VERB_QUERY, "verify of rrset",
|
||||
rrset->rk.dname, ntohs(rrset->rk.type),
|
||||
ntohs(rrset->rk.rrset_class));
|
||||
}
|
||||
setup_sigalg(dnskey, sigalg); /* check all algorithms in the dnskey */
|
||||
sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey, sigalg, &reason);
|
||||
if(vsig) {
|
||||
printf("verify outcome is: %s %s\n", sec_status_to_string(sec),
|
||||
reason?reason:"");
|
||||
}
|
||||
if(should_be_bogus(rrset, qinfo)) {
|
||||
unit_assert(sec == sec_status_bogus);
|
||||
} else {
|
||||
unit_assert(sec == sec_status_secure);
|
||||
}
|
||||
}
|
||||
|
||||
/** verify and test an entry - every rr in the message */
|
||||
static void
|
||||
verifytest_entry(struct entry* e, struct alloc_cache* alloc,
|
||||
struct regional* region, sldns_buffer* pkt,
|
||||
struct ub_packed_rrset_key* dnskey, struct module_env* env,
|
||||
struct val_env* ve)
|
||||
{
|
||||
struct query_info qinfo;
|
||||
struct reply_info* rep = NULL;
|
||||
size_t i;
|
||||
|
||||
regional_free_all(region);
|
||||
if(vsig) {
|
||||
char* s = sldns_wire2str_pkt(e->reply_list->reply_pkt,
|
||||
e->reply_list->reply_len);
|
||||
printf("verifying pkt:\n%s\n", s?s:"outofmemory");
|
||||
free(s);
|
||||
}
|
||||
entry_to_repinfo(e, alloc, region, pkt, &qinfo, &rep);
|
||||
|
||||
for(i=0; i<rep->rrset_count; i++) {
|
||||
verifytest_rrset(env, ve, rep->rrsets[i], dnskey, &qinfo);
|
||||
}
|
||||
|
||||
reply_info_parsedelete(rep, alloc);
|
||||
query_info_clear(&qinfo);
|
||||
}
|
||||
|
||||
/** find RRset in reply by type */
|
||||
static struct ub_packed_rrset_key*
|
||||
find_rrset_type(struct reply_info* rep, uint16_t type)
|
||||
{
|
||||
size_t i;
|
||||
for(i=0; i<rep->rrset_count; i++) {
|
||||
if(ntohs(rep->rrsets[i]->rk.type) == type)
|
||||
return rep->rrsets[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** DS sig test an entry - get DNSKEY and DS in entry and verify */
|
||||
static void
|
||||
dstest_entry(struct entry* e, struct alloc_cache* alloc,
|
||||
struct regional* region, sldns_buffer* pkt, struct module_env* env)
|
||||
{
|
||||
struct query_info qinfo;
|
||||
struct reply_info* rep = NULL;
|
||||
struct ub_packed_rrset_key* ds, *dnskey;
|
||||
int ret;
|
||||
|
||||
regional_free_all(region);
|
||||
if(vsig) {
|
||||
char* s = sldns_wire2str_pkt(e->reply_list->reply_pkt,
|
||||
e->reply_list->reply_len);
|
||||
printf("verifying DS-DNSKEY match:\n%s\n", s?s:"outofmemory");
|
||||
free(s);
|
||||
}
|
||||
entry_to_repinfo(e, alloc, region, pkt, &qinfo, &rep);
|
||||
ds = find_rrset_type(rep, LDNS_RR_TYPE_DS);
|
||||
dnskey = find_rrset_type(rep, LDNS_RR_TYPE_DNSKEY);
|
||||
/* check test is OK */
|
||||
unit_assert(ds && dnskey);
|
||||
|
||||
ret = ds_digest_match_dnskey(env, dnskey, 0, ds, 0);
|
||||
if(strncmp((char*)qinfo.qname, "\003yes", 4) == 0) {
|
||||
if(vsig) {
|
||||
printf("result(yes)= %s\n", ret?"yes":"no");
|
||||
}
|
||||
unit_assert(ret);
|
||||
} else if (strncmp((char*)qinfo.qname, "\002no", 3) == 0) {
|
||||
if(vsig) {
|
||||
printf("result(no)= %s\n", ret?"yes":"no");
|
||||
}
|
||||
unit_assert(!ret);
|
||||
verbose(VERB_QUERY, "DS fail: OK; matched unit test");
|
||||
} else {
|
||||
fatal_exit("Bad qname in DS unit test, yes or no");
|
||||
}
|
||||
|
||||
reply_info_parsedelete(rep, alloc);
|
||||
query_info_clear(&qinfo);
|
||||
}
|
||||
|
||||
/** verify from a file */
|
||||
static void
|
||||
verifytest_file(const char* fname, const char* at_date)
|
||||
{
|
||||
/*
|
||||
* The file contains a list of ldns-testpkts entries.
|
||||
* The first entry must be a query for DNSKEY.
|
||||
* The answer rrset is the keyset that will be used for verification
|
||||
*/
|
||||
struct ub_packed_rrset_key* dnskey;
|
||||
struct regional* region = regional_create();
|
||||
struct alloc_cache alloc;
|
||||
sldns_buffer* buf = sldns_buffer_new(65535);
|
||||
struct entry* e;
|
||||
struct entry* list = read_datafile(fname, 1);
|
||||
struct module_env env;
|
||||
struct val_env ve;
|
||||
time_t now = time(NULL);
|
||||
|
||||
if(!list)
|
||||
fatal_exit("could not read %s: %s", fname, strerror(errno));
|
||||
alloc_init(&alloc, NULL, 1);
|
||||
memset(&env, 0, sizeof(env));
|
||||
memset(&ve, 0, sizeof(ve));
|
||||
env.scratch = region;
|
||||
env.scratch_buffer = buf;
|
||||
env.now = &now;
|
||||
ve.date_override = cfg_convert_timeval(at_date);
|
||||
unit_assert(region && buf);
|
||||
dnskey = extract_keys(list, &alloc, region, buf);
|
||||
if(vsig) log_nametypeclass(VERB_QUERY, "test dnskey",
|
||||
dnskey->rk.dname, ntohs(dnskey->rk.type),
|
||||
ntohs(dnskey->rk.rrset_class));
|
||||
/* ready to go! */
|
||||
for(e = list->next; e; e = e->next) {
|
||||
verifytest_entry(e, &alloc, region, buf, dnskey, &env, &ve);
|
||||
}
|
||||
|
||||
ub_packed_rrset_parsedelete(dnskey, &alloc);
|
||||
delete_entry(list);
|
||||
regional_destroy(region);
|
||||
alloc_clear(&alloc);
|
||||
sldns_buffer_free(buf);
|
||||
}
|
||||
|
||||
/** verify DS matches DNSKEY from a file */
|
||||
static void
|
||||
dstest_file(const char* fname)
|
||||
{
|
||||
/*
|
||||
* The file contains a list of ldns-testpkts entries.
|
||||
* The first entry must be a query for DNSKEY.
|
||||
* The answer rrset is the keyset that will be used for verification
|
||||
*/
|
||||
struct regional* region = regional_create();
|
||||
struct alloc_cache alloc;
|
||||
sldns_buffer* buf = sldns_buffer_new(65535);
|
||||
struct entry* e;
|
||||
struct entry* list = read_datafile(fname, 1);
|
||||
struct module_env env;
|
||||
|
||||
if(!list)
|
||||
fatal_exit("could not read %s: %s", fname, strerror(errno));
|
||||
alloc_init(&alloc, NULL, 1);
|
||||
memset(&env, 0, sizeof(env));
|
||||
env.scratch = region;
|
||||
env.scratch_buffer = buf;
|
||||
unit_assert(region && buf);
|
||||
|
||||
/* ready to go! */
|
||||
for(e = list; e; e = e->next) {
|
||||
dstest_entry(e, &alloc, region, buf, &env);
|
||||
}
|
||||
|
||||
delete_entry(list);
|
||||
regional_destroy(region);
|
||||
alloc_clear(&alloc);
|
||||
sldns_buffer_free(buf);
|
||||
}
|
||||
|
||||
/** helper for unittest of NSEC routines */
|
||||
static int
|
||||
unitest_nsec_has_type_rdata(char* bitmap, size_t len, uint16_t type)
|
||||
{
|
||||
return nsecbitmap_has_type_rdata((uint8_t*)bitmap, len, type);
|
||||
}
|
||||
|
||||
/** Test NSEC type bitmap routine */
|
||||
static void
|
||||
nsectest(void)
|
||||
{
|
||||
/* bitmap starts at type bitmap rdata field */
|
||||
/* from rfc 4034 example */
|
||||
char* bitmap = "\000\006\100\001\000\000\000\003"
|
||||
"\004\033\000\000\000\000\000\000"
|
||||
"\000\000\000\000\000\000\000\000"
|
||||
"\000\000\000\000\000\000\000\000"
|
||||
"\000\000\000\000\040";
|
||||
size_t len = 37;
|
||||
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 0));
|
||||
unit_assert(unitest_nsec_has_type_rdata(bitmap, len, LDNS_RR_TYPE_A));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 2));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 3));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 4));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 5));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 6));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 7));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 8));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 9));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 10));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 11));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 12));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 13));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 14));
|
||||
unit_assert(unitest_nsec_has_type_rdata(bitmap, len, LDNS_RR_TYPE_MX));
|
||||
unit_assert(unitest_nsec_has_type_rdata(bitmap, len, LDNS_RR_TYPE_RRSIG));
|
||||
unit_assert(unitest_nsec_has_type_rdata(bitmap, len, LDNS_RR_TYPE_NSEC));
|
||||
unit_assert(unitest_nsec_has_type_rdata(bitmap, len, 1234));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1233));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1235));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1236));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1237));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1238));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1239));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1240));
|
||||
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 2230));
|
||||
}
|
||||
|
||||
/** Test hash algo - NSEC3 hash it and compare result */
|
||||
static void
|
||||
nsec3_hash_test_entry(struct entry* e, rbtree_t* ct,
|
||||
struct alloc_cache* alloc, struct regional* region,
|
||||
sldns_buffer* buf)
|
||||
{
|
||||
struct query_info qinfo;
|
||||
struct reply_info* rep = NULL;
|
||||
struct ub_packed_rrset_key* answer, *nsec3;
|
||||
struct nsec3_cached_hash* hash = NULL;
|
||||
int ret;
|
||||
uint8_t* qname;
|
||||
|
||||
if(vsig) {
|
||||
char* s = sldns_wire2str_pkt(e->reply_list->reply_pkt,
|
||||
e->reply_list->reply_len);
|
||||
printf("verifying NSEC3 hash:\n%s\n", s?s:"outofmemory");
|
||||
free(s);
|
||||
}
|
||||
entry_to_repinfo(e, alloc, region, buf, &qinfo, &rep);
|
||||
nsec3 = find_rrset_type(rep, LDNS_RR_TYPE_NSEC3);
|
||||
answer = find_rrset_type(rep, LDNS_RR_TYPE_AAAA);
|
||||
qname = regional_alloc_init(region, qinfo.qname, qinfo.qname_len);
|
||||
/* check test is OK */
|
||||
unit_assert(nsec3 && answer && qname);
|
||||
|
||||
ret = nsec3_hash_name(ct, region, buf, nsec3, 0, qname,
|
||||
qinfo.qname_len, &hash);
|
||||
if(ret != 1) {
|
||||
printf("Bad nsec3_hash_name retcode %d\n", ret);
|
||||
unit_assert(ret == 1);
|
||||
}
|
||||
unit_assert(hash->dname && hash->hash && hash->hash_len &&
|
||||
hash->b32 && hash->b32_len);
|
||||
unit_assert(hash->b32_len == (size_t)answer->rk.dname[0]);
|
||||
/* does not do lowercasing. */
|
||||
unit_assert(memcmp(hash->b32, answer->rk.dname+1, hash->b32_len)
|
||||
== 0);
|
||||
|
||||
reply_info_parsedelete(rep, alloc);
|
||||
query_info_clear(&qinfo);
|
||||
}
|
||||
|
||||
|
||||
/** Read file to test NSEC3 hash algo */
|
||||
static void
|
||||
nsec3_hash_test(const char* fname)
|
||||
{
|
||||
/*
|
||||
* The list contains a list of ldns-testpkts entries.
|
||||
* Every entry is a test.
|
||||
* The qname is hashed.
|
||||
* The answer section AAAA RR name is the required result.
|
||||
* The auth section NSEC3 is used to get hash parameters.
|
||||
* The hash cache is maintained per file.
|
||||
*
|
||||
* The test does not perform canonicalization during the compare.
|
||||
*/
|
||||
rbtree_t ct;
|
||||
struct regional* region = regional_create();
|
||||
struct alloc_cache alloc;
|
||||
sldns_buffer* buf = sldns_buffer_new(65535);
|
||||
struct entry* e;
|
||||
struct entry* list = read_datafile(fname, 1);
|
||||
|
||||
if(!list)
|
||||
fatal_exit("could not read %s: %s", fname, strerror(errno));
|
||||
rbtree_init(&ct, &nsec3_hash_cmp);
|
||||
alloc_init(&alloc, NULL, 1);
|
||||
unit_assert(region && buf);
|
||||
|
||||
/* ready to go! */
|
||||
for(e = list; e; e = e->next) {
|
||||
nsec3_hash_test_entry(e, &ct, &alloc, region, buf);
|
||||
}
|
||||
|
||||
delete_entry(list);
|
||||
regional_destroy(region);
|
||||
alloc_clear(&alloc);
|
||||
sldns_buffer_free(buf);
|
||||
}
|
||||
|
||||
void
|
||||
verify_test(void)
|
||||
{
|
||||
unit_show_feature("signature verify");
|
||||
verifytest_file("testdata/test_signatures.1", "20070818005004");
|
||||
verifytest_file("testdata/test_signatures.2", "20080414005004");
|
||||
verifytest_file("testdata/test_signatures.3", "20080416005004");
|
||||
verifytest_file("testdata/test_signatures.4", "20080416005004");
|
||||
verifytest_file("testdata/test_signatures.5", "20080416005004");
|
||||
verifytest_file("testdata/test_signatures.6", "20080416005004");
|
||||
verifytest_file("testdata/test_signatures.7", "20070829144150");
|
||||
verifytest_file("testdata/test_signatures.8", "20070829144150");
|
||||
#if (defined(HAVE_EVP_SHA256) || defined(HAVE_NSS)) && defined(USE_SHA2)
|
||||
verifytest_file("testdata/test_sigs.rsasha256", "20070829144150");
|
||||
verifytest_file("testdata/test_sigs.sha1_and_256", "20070829144150");
|
||||
verifytest_file("testdata/test_sigs.rsasha256_draft", "20090101000000");
|
||||
#endif
|
||||
#if (defined(HAVE_EVP_SHA512) || defined(HAVE_NSS)) && defined(USE_SHA2)
|
||||
verifytest_file("testdata/test_sigs.rsasha512_draft", "20070829144150");
|
||||
#endif
|
||||
verifytest_file("testdata/test_sigs.hinfo", "20090107100022");
|
||||
verifytest_file("testdata/test_sigs.revoked", "20080414005004");
|
||||
#ifdef USE_GOST
|
||||
if(sldns_key_EVP_load_gost_id())
|
||||
verifytest_file("testdata/test_sigs.gost", "20090807060504");
|
||||
else printf("Warning: skipped GOST, openssl does not provide gost.\n");
|
||||
#endif
|
||||
#ifdef USE_ECDSA
|
||||
/* test for support in case we use libNSS and ECC is removed */
|
||||
if(dnskey_algo_id_is_supported(LDNS_ECDSAP256SHA256)) {
|
||||
verifytest_file("testdata/test_sigs.ecdsa_p256", "20100908100439");
|
||||
verifytest_file("testdata/test_sigs.ecdsa_p384", "20100908100439");
|
||||
}
|
||||
dstest_file("testdata/test_ds.sha384");
|
||||
#endif
|
||||
dstest_file("testdata/test_ds.sha1");
|
||||
nsectest();
|
||||
nsec3_hash_test("testdata/test_nsec3_hash.1");
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue