added unbound to external deps

This commit is contained in:
Riccardo Spagni 2014-10-05 23:44:31 +02:00
parent 732493c5cb
commit 9ef094b356
No known key found for this signature in database
GPG key ID: 55432DF31CCD4FCD
394 changed files with 199264 additions and 0 deletions

523
external/unbound/testcode/asynclook.c vendored Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

61
external/unbound/testcode/do-tests.sh vendored Executable file
View 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

File diff suppressed because it is too large Load diff

75
external/unbound/testcode/fake_event.h vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

458
external/unbound/testcode/replay.h vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

268
external/unbound/testcode/testpkts.h vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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");
}