Home | History | Annotate | Line # | Download | only in testcode
      1 /*
      2  * testcode/asynclook.c - debug program perform async libunbound queries.
      3  *
      4  * Copyright (c) 2008, NLnet Labs. All rights reserved.
      5  *
      6  * This software is open source.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  * Redistributions of source code must retain the above copyright notice,
     13  * this list of conditions and the following disclaimer.
     14  *
     15  * Redistributions in binary form must reproduce the above copyright notice,
     16  * this list of conditions and the following disclaimer in the documentation
     17  * and/or other materials provided with the distribution.
     18  *
     19  * Neither the name of the NLNET LABS nor the names of its contributors may
     20  * be used to endorse or promote products derived from this software without
     21  * specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     27  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
     29  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     34  */
     35 
     36 /**
     37  * \file
     38  *
     39  * This program shows the results from several background lookups,
     40  * while printing time in the foreground.
     41  */
     42 
     43 #include "config.h"
     44 #ifdef HAVE_GETOPT_H
     45 #include <getopt.h>
     46 #endif
     47 #include "libunbound/unbound.h"
     48 #include "libunbound/context.h"
     49 #include "util/locks.h"
     50 #include "util/log.h"
     51 #include "sldns/rrdef.h"
     52 #ifdef UNBOUND_ALLOC_LITE
     53 #undef malloc
     54 #undef calloc
     55 #undef realloc
     56 #undef free
     57 #undef strdup
     58 #endif
     59 #ifdef HAVE_SSL
     60 #ifdef HAVE_OPENSSL_SSL_H
     61 #include <openssl/ssl.h>
     62 #endif
     63 #ifdef HAVE_OPENSSL_ERR_H
     64 #include <openssl/err.h>
     65 #endif
     66 #endif /* HAVE_SSL */
     67 
     68 
     69 /** keeping track of the async ids */
     70 struct track_id {
     71 	/** the id to pass to libunbound to cancel */
     72 	int id;
     73 	/** true if cancelled */
     74 	int cancel;
     75 	/** a lock on this structure for thread safety */
     76 	lock_basic_type lock;
     77 };
     78 
     79 /**
     80  * result list for the lookups
     81  */
     82 struct lookinfo {
     83 	/** name to look up */
     84 	char* name;
     85 	/** tracking number that can be used to cancel the query */
     86 	int async_id;
     87 	/** error code from libunbound */
     88 	int err;
     89 	/** result from lookup */
     90 	struct ub_result* result;
     91 };
     92 
     93 /** global variable to see how many queries we have left */
     94 static int num_wait = 0;
     95 
     96 /** usage information for asynclook */
     97 static void usage(char* argv[])
     98 {
     99 	printf("usage: %s [options] name ...\n", argv[0]);
    100 	printf("names are looked up at the same time, asynchronously.\n");
    101 	printf("	-b : use blocking requests\n");
    102 	printf("	-c : cancel the requests\n");
    103 	printf("	-d : enable debug output\n");
    104 	printf("	-f addr : use addr, forward to that server\n");
    105 	printf("	-h : this help message\n");
    106 	printf("	-H fname : read hosts from fname\n");
    107 	printf("	-r fname : read resolv.conf from fname\n");
    108 	printf("	-t : use a resolver thread instead of forking a process\n");
    109 	printf("	-x : perform extended threaded test\n");
    110 	exit(1);
    111 }
    112 
    113 /** print result from lookup nicely */
    114 static void
    115 print_result(struct lookinfo* info)
    116 {
    117 	char buf[100];
    118 	if(info->err) /* error (from libunbound) */
    119 		printf("%s: error %s\n", info->name,
    120 			ub_strerror(info->err));
    121 	else if(!info->result)
    122 		printf("%s: cancelled\n", info->name);
    123 	else if(info->result->havedata)
    124 		printf("%s: %s\n", info->name,
    125 			inet_ntop(AF_INET, info->result->data[0],
    126 			buf, (socklen_t)sizeof(buf)));
    127 	else {
    128 		/* there is no data, why that? */
    129 		if(info->result->rcode == 0 /*noerror*/ ||
    130 			info->result->nxdomain)
    131 			printf("%s: no data %s\n", info->name,
    132 			info->result->nxdomain?"(no such host)":
    133 			"(no IP4 address)");
    134 		else	/* some error (from the server) */
    135 			printf("%s: DNS error %d\n", info->name,
    136 				info->result->rcode);
    137 	}
    138 }
    139 
    140 /** this is a function of type ub_callback_t */
    141 static void
    142 lookup_is_done(void* mydata, int err, struct ub_result* result)
    143 {
    144 	/* cast mydata back to the correct type */
    145 	struct lookinfo* info = (struct lookinfo*)mydata;
    146 	fprintf(stderr, "name %s resolved\n", info->name);
    147 	info->err = err;
    148 	info->result = result;
    149 	/* one less to wait for */
    150 	num_wait--;
    151 }
    152 
    153 /** check error, if bad, exit with error message */
    154 static void
    155 checkerr(const char* desc, int err)
    156 {
    157 	if(err != 0) {
    158 		printf("%s error: %s\n", desc, ub_strerror(err));
    159 		exit(1);
    160 	}
    161 }
    162 
    163 #ifdef THREADS_DISABLED
    164 /** only one process can communicate with async worker */
    165 #define NUMTHR 1
    166 #else /* have threads */
    167 /** number of threads to make in extended test */
    168 #define NUMTHR 10
    169 #endif
    170 
    171 /** struct for extended thread info */
    172 struct ext_thr_info {
    173 	/** thread num for debug */
    174 	int thread_num;
    175 	/** thread id */
    176 	ub_thread_type tid;
    177 	/** context */
    178 	struct ub_ctx* ctx;
    179 	/** size of array to query */
    180 	int argc;
    181 	/** array of names to query */
    182 	char** argv;
    183 	/** number of queries to do */
    184 	int numq;
    185 	/** list of ids to free once threads are done */
    186 	struct track_id* id_list;
    187 };
    188 
    189 /** if true, we are testing against 'localhost' and extra checking is done */
    190 static int q_is_localhost = 0;
    191 
    192 /** check result structure for the 'correct' answer */
    193 static void
    194 ext_check_result(const char* desc, int err, struct ub_result* result)
    195 {
    196 	checkerr(desc, err);
    197 	if(result == NULL) {
    198 		printf("%s: error result is NULL.\n", desc);
    199 		exit(1);
    200 	}
    201 	if(q_is_localhost) {
    202 		if(strcmp(result->qname, "localhost") != 0) {
    203 			printf("%s: error result has wrong qname.\n", desc);
    204 			exit(1);
    205 		}
    206 		if(result->qtype != LDNS_RR_TYPE_A) {
    207 			printf("%s: error result has wrong qtype.\n", desc);
    208 			exit(1);
    209 		}
    210 		if(result->qclass != LDNS_RR_CLASS_IN) {
    211 			printf("%s: error result has wrong qclass.\n", desc);
    212 			exit(1);
    213 		}
    214 		if(result->data == NULL) {
    215 			printf("%s: error result->data is NULL.\n", desc);
    216 			exit(1);
    217 		}
    218 		if(result->len == NULL) {
    219 			printf("%s: error result->len is NULL.\n", desc);
    220 			exit(1);
    221 		}
    222 		if(result->rcode != 0) {
    223 			printf("%s: error result->rcode is set.\n", desc);
    224 			exit(1);
    225 		}
    226 		if(result->havedata == 0) {
    227 			printf("%s: error result->havedata is unset.\n", desc);
    228 			exit(1);
    229 		}
    230 		if(result->nxdomain != 0) {
    231 			printf("%s: error result->nxdomain is set.\n", desc);
    232 			exit(1);
    233 		}
    234 		if(result->secure || result->bogus) {
    235 			printf("%s: error result->secure or bogus is set.\n",
    236 				desc);
    237 			exit(1);
    238 		}
    239 		if(result->data[0] == NULL) {
    240 			printf("%s: error result->data[0] is NULL.\n", desc);
    241 			exit(1);
    242 		}
    243 		if(result->len[0] != 4) {
    244 			printf("%s: error result->len[0] is wrong.\n", desc);
    245 			exit(1);
    246 		}
    247 		if(result->len[1] != 0 || result->data[1] != NULL) {
    248 			printf("%s: error result->data[1] or len[1] is "
    249 				"wrong.\n", desc);
    250 			exit(1);
    251 		}
    252 		if(result->answer_packet == NULL) {
    253 			printf("%s: error result->answer_packet is NULL.\n",
    254 				desc);
    255 			exit(1);
    256 		}
    257 		if(result->answer_len != 54) {
    258 			printf("%s: error result->answer_len is wrong.\n",
    259 				desc);
    260 			exit(1);
    261 		}
    262 	}
    263 }
    264 
    265 /** extended bg result callback, this function is ub_callback_t */
    266 static void
    267 ext_callback(void* mydata, int err, struct ub_result* result)
    268 {
    269 	struct track_id* my_id = (struct track_id*)mydata;
    270 	int doprint = 0;
    271 	if(my_id) {
    272 		/* I have an id, make sure we are not cancelled */
    273 		lock_basic_lock(&my_id->lock);
    274 		if(doprint)
    275 			printf("cb %d: ", my_id->id);
    276 		if(my_id->cancel) {
    277 			printf("error: query id=%d returned, but was cancelled\n",
    278 				my_id->id);
    279 			abort();
    280 			exit(1);
    281 		}
    282 		lock_basic_unlock(&my_id->lock);
    283 	}
    284 	ext_check_result("ext_callback", err, result);
    285 	log_assert(result);
    286 	if(doprint) {
    287 		struct lookinfo pi;
    288 		pi.name = result?result->qname:"noname";
    289 		pi.result = result;
    290 		pi.err = 0;
    291 		print_result(&pi);
    292 	}
    293 	ub_resolve_free(result);
    294 }
    295 
    296 /** extended thread worker */
    297 static void*
    298 ext_thread(void* arg)
    299 {
    300 	struct ext_thr_info* inf = (struct ext_thr_info*)arg;
    301 	int i, r;
    302 	struct ub_result* result;
    303 	struct track_id* async_ids = NULL;
    304 	log_thread_set(&inf->thread_num);
    305 	if(inf->thread_num > NUMTHR*2/3) {
    306 		async_ids = (struct track_id*)calloc((size_t)inf->numq, sizeof(struct track_id));
    307 		if(!async_ids) {
    308 			printf("out of memory\n");
    309 			exit(1);
    310 		}
    311 		for(i=0; i<inf->numq; i++) {
    312 			lock_basic_init(&async_ids[i].lock);
    313 		}
    314 		inf->id_list = async_ids;
    315 	}
    316 	for(i=0; i<inf->numq; i++) {
    317 		if(async_ids) {
    318 			r = ub_resolve_async(inf->ctx,
    319 				inf->argv[i%inf->argc], LDNS_RR_TYPE_A,
    320 				LDNS_RR_CLASS_IN, &async_ids[i], ext_callback,
    321 				&async_ids[i].id);
    322 			checkerr("ub_resolve_async", r);
    323 			if(i > 100) {
    324 				lock_basic_lock(&async_ids[i-100].lock);
    325 				r = ub_cancel(inf->ctx, async_ids[i-100].id);
    326 				if(r != UB_NOID)
    327 					async_ids[i-100].cancel=1;
    328 				lock_basic_unlock(&async_ids[i-100].lock);
    329 				if(r != UB_NOID)
    330 					checkerr("ub_cancel", r);
    331 			}
    332 		} else if(inf->thread_num > NUMTHR/2) {
    333 			/* async */
    334 			r = ub_resolve_async(inf->ctx,
    335 				inf->argv[i%inf->argc], LDNS_RR_TYPE_A,
    336 				LDNS_RR_CLASS_IN, NULL, ext_callback, NULL);
    337 			checkerr("ub_resolve_async", r);
    338 		} else  {
    339 			/* blocking */
    340 			r = ub_resolve(inf->ctx, inf->argv[i%inf->argc],
    341 				LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN, &result);
    342 			ext_check_result("ub_resolve", r, result);
    343 			ub_resolve_free(result);
    344 		}
    345 	}
    346 	if(inf->thread_num > NUMTHR/2) {
    347 		r = ub_wait(inf->ctx);
    348 		checkerr("ub_ctx_wait", r);
    349 	}
    350 	/* if these locks are destroyed, or if the async_ids is freed, then
    351 	   a use-after-free happens in another thread.
    352 	   The allocation is only part of this test, though. */
    353 
    354 	return NULL;
    355 }
    356 
    357 /** perform extended threaded test */
    358 static int
    359 ext_test(struct ub_ctx* ctx, int argc, char** argv)
    360 {
    361 	struct ext_thr_info inf[NUMTHR];
    362 	int i;
    363 	if(argc == 1 && strcmp(argv[0], "localhost") == 0)
    364 		q_is_localhost = 1;
    365 	printf("extended test start (%d threads)\n", NUMTHR);
    366 	for(i=0; i<NUMTHR; i++) {
    367 		/* 0 = this, 1 = library bg worker */
    368 		inf[i].thread_num = i+2;
    369 		inf[i].ctx = ctx;
    370 		inf[i].argc = argc;
    371 		inf[i].argv = argv;
    372 		inf[i].numq = 100;
    373 		inf[i].id_list = NULL;
    374 		ub_thread_create(&inf[i].tid, ext_thread, &inf[i]);
    375 	}
    376 	/* the work happens here */
    377 	for(i=0; i<NUMTHR; i++) {
    378 		ub_thread_join(inf[i].tid);
    379 	}
    380 	printf("extended test end\n");
    381 	/* free the id lists */
    382 	for(i=0; i<NUMTHR; i++) {
    383 		if(inf[i].id_list) {
    384 			int j;
    385 			for(j=0; j<inf[i].numq; j++) {
    386 				lock_basic_destroy(&inf[i].id_list[j].lock);
    387 			}
    388 			free(inf[i].id_list);
    389 		}
    390 	}
    391 	ub_ctx_delete(ctx);
    392 	checklock_stop();
    393 	return 0;
    394 }
    395 
    396 /** getopt global, in case header files fail to declare it. */
    397 extern int optind;
    398 /** getopt global, in case header files fail to declare it. */
    399 extern char* optarg;
    400 
    401 /** main program for asynclook */
    402 int main(int argc, char** argv)
    403 {
    404 	int c;
    405 	struct ub_ctx* ctx;
    406 	struct lookinfo* lookups;
    407 	int i, r, cancel=0, blocking=0, ext=0;
    408 
    409 	checklock_start();
    410 	/* init log now because solaris thr_key_create() is not threadsafe */
    411 	log_init(0,0,0);
    412 	/* lock debug start (if any) */
    413 
    414 	/* create context */
    415 	ctx = ub_ctx_create();
    416 	if(!ctx) {
    417 		printf("could not create context, %s\n", strerror(errno));
    418 		return 1;
    419 	}
    420 
    421 	/* command line options */
    422 	if(argc == 1) {
    423 		usage(argv);
    424 	}
    425 	while( (c=getopt(argc, argv, "bcdf:hH:r:tx")) != -1) {
    426 		switch(c) {
    427 			case 'd':
    428 				r = ub_ctx_debuglevel(ctx, 3);
    429 				checkerr("ub_ctx_debuglevel", r);
    430 				break;
    431 			case 't':
    432 				r = ub_ctx_async(ctx, 1);
    433 				checkerr("ub_ctx_async", r);
    434 				break;
    435 			case 'c':
    436 				cancel = 1;
    437 				break;
    438 			case 'b':
    439 				blocking = 1;
    440 				break;
    441 			case 'r':
    442 				r = ub_ctx_resolvconf(ctx, optarg);
    443 				if(r != 0) {
    444 					printf("ub_ctx_resolvconf "
    445 						"error: %s : %s\n",
    446 						ub_strerror(r),
    447 						strerror(errno));
    448 					return 1;
    449 				}
    450 				break;
    451 			case 'H':
    452 				r = ub_ctx_hosts(ctx, optarg);
    453 				if(r != 0) {
    454 					printf("ub_ctx_hosts "
    455 						"error: %s : %s\n",
    456 						ub_strerror(r),
    457 						strerror(errno));
    458 					return 1;
    459 				}
    460 				break;
    461 			case 'f':
    462 				r = ub_ctx_set_fwd(ctx, optarg);
    463 				checkerr("ub_ctx_set_fwd", r);
    464 				break;
    465 			case 'x':
    466 				ext = 1;
    467 				break;
    468 			case 'h':
    469 			case '?':
    470 			default:
    471 				usage(argv);
    472 		}
    473 	}
    474 	argc -= optind;
    475 	argv += optind;
    476 
    477 #ifdef HAVE_SSL
    478 #ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS
    479 	ERR_load_crypto_strings();
    480 #endif
    481 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
    482 	ERR_load_SSL_strings();
    483 #endif
    484 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO)
    485 #  ifndef S_SPLINT_S
    486 	OpenSSL_add_all_algorithms();
    487 #  endif
    488 #else
    489 	OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
    490 		| OPENSSL_INIT_ADD_ALL_DIGESTS
    491 		| OPENSSL_INIT_LOAD_CRYPTO_STRINGS
    492 #  if defined(OPENSSL_INIT_NO_LOAD_CONFIG) && defined(UB_ON_WINDOWS)
    493 		| OPENSSL_INIT_NO_LOAD_CONFIG
    494 #  endif
    495 		, NULL);
    496 #endif
    497 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
    498 	(void)SSL_library_init();
    499 #else
    500 	(void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS
    501 #  if defined(OPENSSL_INIT_NO_LOAD_CONFIG) && defined(UB_ON_WINDOWS)
    502 		| OPENSSL_INIT_NO_LOAD_CONFIG
    503 #  endif
    504 		, NULL);
    505 #endif
    506 #endif /* HAVE_SSL */
    507 
    508 	if(ext)
    509 		return ext_test(ctx, argc, argv);
    510 
    511 	/* allocate array for results. */
    512 	lookups = (struct lookinfo*)calloc((size_t)argc,
    513 		sizeof(struct lookinfo));
    514 	if(!lookups) {
    515 		printf("out of memory\n");
    516 		return 1;
    517 	}
    518 
    519 	/* perform asynchronous calls */
    520 	num_wait = argc;
    521 	for(i=0; i<argc; i++) {
    522 		lookups[i].name = argv[i];
    523 		if(blocking) {
    524 			fprintf(stderr, "lookup %s\n", argv[i]);
    525 			r = ub_resolve(ctx, argv[i], LDNS_RR_TYPE_A,
    526 				LDNS_RR_CLASS_IN, &lookups[i].result);
    527 			checkerr("ub_resolve", r);
    528 		} else {
    529 			fprintf(stderr, "start async lookup %s\n", argv[i]);
    530 			r = ub_resolve_async(ctx, argv[i], LDNS_RR_TYPE_A,
    531 				LDNS_RR_CLASS_IN, &lookups[i], &lookup_is_done,
    532 				&lookups[i].async_id);
    533 			checkerr("ub_resolve_async", r);
    534 		}
    535 	}
    536 	if(blocking)
    537 		num_wait = 0;
    538 	else if(cancel) {
    539 		for(i=0; i<argc; i++) {
    540 			fprintf(stderr, "cancel %s\n", argv[i]);
    541 			r = ub_cancel(ctx, lookups[i].async_id);
    542 			if(r != UB_NOID)
    543 				checkerr("ub_cancel", r);
    544 		}
    545 		num_wait = 0;
    546 	}
    547 
    548 	/* wait while the hostnames are looked up. Do something useful here */
    549 	if(num_wait > 0)
    550 	    for(i=0; i<1000; i++) {
    551 		usleep(100000);
    552 		fprintf(stderr, "%g seconds passed\n", 0.1*(double)i);
    553 		r = ub_process(ctx);
    554 		checkerr("ub_process", r);
    555 		if(num_wait == 0)
    556 			break;
    557 	}
    558 	if(i>=999) {
    559 		printf("timed out\n");
    560 		return 0;
    561 	}
    562 	printf("lookup complete\n");
    563 
    564 	/* print lookup results */
    565 	for(i=0; i<argc; i++) {
    566 		print_result(&lookups[i]);
    567 		ub_resolve_free(lookups[i].result);
    568 	}
    569 
    570 	ub_ctx_delete(ctx);
    571 	free(lookups);
    572 	checklock_stop();
    573 	return 0;
    574 }
    575