danicoin/external/unbound/testcode/asynclook.c
Erik de Castro Lopo a85b5759f3 Upgrade unbound library
These files were pulled from the 1.6.3 release tarball.

This new version builds against OpenSSL version 1.1 which will be
the default in the new Debian Stable which is due to be released
RealSoonNow (tm).
2017-06-17 23:04:00 +10:00

529 lines
13 KiB
C

/*
* 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 "sldns/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_type 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_type 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 these locks are destroyed, or if the async_ids is freed, then
a use-after-free happens in another thread.
The allocation is only part of this test, though. */
/*
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 asynchronous 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;
}