2 * testcode/asynclook.c - debug program perform async libunbound queries.
4 * Copyright (c) 2008, NLnet Labs. All rights reserved.
6 * This software is open source.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
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.
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.
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.
39 * This program shows the results from several background lookups,
40 * while printing time in the foreground.
47 #include "libunbound/unbound.h"
48 #include "libunbound/context.h"
49 #include "util/locks.h"
51 #include "sldns/rrdef.h"
52 #ifdef UNBOUND_ALLOC_LITE
60 #ifdef HAVE_OPENSSL_SSL_H
61 #include <openssl/ssl.h>
63 #ifdef HAVE_OPENSSL_ERR_H
64 #include <openssl/err.h>
69 /** keeping track of the async ids */
71 /** the id to pass to libunbound to cancel */
73 /** true if cancelled */
75 /** a lock on this structure for thread safety */
80 * result list for the lookups
83 /** name to look up */
85 /** tracking number that can be used to cancel the query */
87 /** error code from libunbound */
89 /** result from lookup */
90 struct ub_result* result;
93 /** global variable to see how many queries we have left */
94 static int num_wait = 0;
96 /** usage information for asynclook */
97 static void usage(char* argv[])
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");
113 /** print result from lookup nicely */
115 print_result(struct lookinfo* info)
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)));
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)":
134 else /* some error (from the server) */
135 printf("%s: DNS error %d\n", info->name,
136 info->result->rcode);
140 /** this is a function of type ub_callback_t */
142 lookup_is_done(void* mydata, int err, struct ub_result* result)
144 /* cast mydata back to the correct type */
145 struct lookinfo* info = (struct lookinfo*)mydata;
146 fprintf(stderr, "name %s resolved\n", info->name);
148 info->result = result;
149 /* one less to wait for */
153 /** check error, if bad, exit with error message */
155 checkerr(const char* desc, int err)
158 printf("%s error: %s\n", desc, ub_strerror(err));
163 #ifdef THREADS_DISABLED
164 /** only one process can communicate with async worker */
166 #else /* have threads */
167 /** number of threads to make in extended test */
171 /** struct for extended thread info */
172 struct ext_thr_info {
173 /** thread num for debug */
179 /** size of array to query */
181 /** array of names to query */
183 /** number of queries to do */
187 /** if true, we are testing against 'localhost' and extra checking is done */
188 static int q_is_localhost = 0;
190 /** check result structure for the 'correct' answer */
192 ext_check_result(const char* desc, int err, struct ub_result* result)
196 printf("%s: error result is NULL.\n", desc);
200 if(strcmp(result->qname, "localhost") != 0) {
201 printf("%s: error result has wrong qname.\n", desc);
204 if(result->qtype != LDNS_RR_TYPE_A) {
205 printf("%s: error result has wrong qtype.\n", desc);
208 if(result->qclass != LDNS_RR_CLASS_IN) {
209 printf("%s: error result has wrong qclass.\n", desc);
212 if(result->data == NULL) {
213 printf("%s: error result->data is NULL.\n", desc);
216 if(result->len == NULL) {
217 printf("%s: error result->len is NULL.\n", desc);
220 if(result->rcode != 0) {
221 printf("%s: error result->rcode is set.\n", desc);
224 if(result->havedata == 0) {
225 printf("%s: error result->havedata is unset.\n", desc);
228 if(result->nxdomain != 0) {
229 printf("%s: error result->nxdomain is set.\n", desc);
232 if(result->secure || result->bogus) {
233 printf("%s: error result->secure or bogus is set.\n",
237 if(result->data[0] == NULL) {
238 printf("%s: error result->data[0] is NULL.\n", desc);
241 if(result->len[0] != 4) {
242 printf("%s: error result->len[0] is wrong.\n", desc);
245 if(result->len[1] != 0 || result->data[1] != NULL) {
246 printf("%s: error result->data[1] or len[1] is "
250 if(result->answer_packet == NULL) {
251 printf("%s: error result->answer_packet is NULL.\n",
255 if(result->answer_len != 54) {
256 printf("%s: error result->answer_len is wrong.\n",
263 /** extended bg result callback, this function is ub_callback_t */
265 ext_callback(void* mydata, int err, struct ub_result* result)
267 struct track_id* my_id = (struct track_id*)mydata;
270 /* I have an id, make sure we are not cancelled */
271 lock_basic_lock(&my_id->lock);
273 printf("cb %d: ", my_id->id);
275 printf("error: query id=%d returned, but was cancelled\n",
280 lock_basic_unlock(&my_id->lock);
282 ext_check_result("ext_callback", err, result);
286 pi.name = result?result->qname:"noname";
291 ub_resolve_free(result);
294 /** extended thread worker */
296 ext_thread(void* arg)
298 struct ext_thr_info* inf = (struct ext_thr_info*)arg;
300 struct ub_result* result;
301 struct track_id* async_ids = NULL;
302 log_thread_set(&inf->thread_num);
303 if(inf->thread_num > NUMTHR*2/3) {
304 async_ids = (struct track_id*)calloc((size_t)inf->numq, sizeof(struct track_id));
306 printf("out of memory\n");
309 for(i=0; i<inf->numq; i++) {
310 lock_basic_init(&async_ids[i].lock);
313 for(i=0; i<inf->numq; i++) {
315 r = ub_resolve_async(inf->ctx,
316 inf->argv[i%inf->argc], LDNS_RR_TYPE_A,
317 LDNS_RR_CLASS_IN, &async_ids[i], ext_callback,
319 checkerr("ub_resolve_async", r);
321 lock_basic_lock(&async_ids[i-100].lock);
322 r = ub_cancel(inf->ctx, async_ids[i-100].id);
324 async_ids[i-100].cancel=1;
325 lock_basic_unlock(&async_ids[i-100].lock);
327 checkerr("ub_cancel", r);
329 } else if(inf->thread_num > NUMTHR/2) {
331 r = ub_resolve_async(inf->ctx,
332 inf->argv[i%inf->argc], LDNS_RR_TYPE_A,
333 LDNS_RR_CLASS_IN, NULL, ext_callback, NULL);
334 checkerr("ub_resolve_async", r);
337 r = ub_resolve(inf->ctx, inf->argv[i%inf->argc],
338 LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN, &result);
339 ext_check_result("ub_resolve", r, result);
340 ub_resolve_free(result);
343 if(inf->thread_num > NUMTHR/2) {
344 r = ub_wait(inf->ctx);
345 checkerr("ub_ctx_wait", r);
347 /* if these locks are destroyed, or if the async_ids is freed, then
348 a use-after-free happens in another thread.
349 The allocation is only part of this test, though. */
352 for(i=0; i<inf->numq; i++) {
353 lock_basic_destroy(&async_ids[i].lock);
362 /** perform extended threaded test */
364 ext_test(struct ub_ctx* ctx, int argc, char** argv)
366 struct ext_thr_info inf[NUMTHR];
368 if(argc == 1 && strcmp(argv[0], "localhost") == 0)
370 printf("extended test start (%d threads)\n", NUMTHR);
371 for(i=0; i<NUMTHR; i++) {
372 /* 0 = this, 1 = library bg worker */
373 inf[i].thread_num = i+2;
378 ub_thread_create(&inf[i].tid, ext_thread, &inf[i]);
380 /* the work happens here */
381 for(i=0; i<NUMTHR; i++) {
382 ub_thread_join(inf[i].tid);
384 printf("extended test end\n");
390 /** getopt global, in case header files fail to declare it. */
392 /** getopt global, in case header files fail to declare it. */
395 /** main program for asynclook */
396 int main(int argc, char** argv)
400 struct lookinfo* lookups;
401 int i, r, cancel=0, blocking=0, ext=0;
403 /* init log now because solaris thr_key_create() is not threadsafe */
405 /* lock debug start (if any) */
409 ctx = ub_ctx_create();
411 printf("could not create context, %s\n", strerror(errno));
415 /* command line options */
419 while( (c=getopt(argc, argv, "bcdf:hH:r:tx")) != -1) {
422 r = ub_ctx_debuglevel(ctx, 3);
423 checkerr("ub_ctx_debuglevel", r);
426 r = ub_ctx_async(ctx, 1);
427 checkerr("ub_ctx_async", r);
436 r = ub_ctx_resolvconf(ctx, optarg);
438 printf("ub_ctx_resolvconf "
446 r = ub_ctx_hosts(ctx, optarg);
448 printf("ub_ctx_hosts "
456 r = ub_ctx_set_fwd(ctx, optarg);
457 checkerr("ub_ctx_set_fwd", r);
472 #ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS
473 ERR_load_crypto_strings();
475 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
476 ERR_load_SSL_strings();
478 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO)
479 OpenSSL_add_all_algorithms();
481 OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
482 | OPENSSL_INIT_ADD_ALL_DIGESTS
483 | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
485 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
486 (void)SSL_library_init();
488 (void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
490 #endif /* HAVE_SSL */
493 return ext_test(ctx, argc, argv);
495 /* allocate array for results. */
496 lookups = (struct lookinfo*)calloc((size_t)argc,
497 sizeof(struct lookinfo));
499 printf("out of memory\n");
503 /* perform asynchronous calls */
505 for(i=0; i<argc; i++) {
506 lookups[i].name = argv[i];
508 fprintf(stderr, "lookup %s\n", argv[i]);
509 r = ub_resolve(ctx, argv[i], LDNS_RR_TYPE_A,
510 LDNS_RR_CLASS_IN, &lookups[i].result);
511 checkerr("ub_resolve", r);
513 fprintf(stderr, "start async lookup %s\n", argv[i]);
514 r = ub_resolve_async(ctx, argv[i], LDNS_RR_TYPE_A,
515 LDNS_RR_CLASS_IN, &lookups[i], &lookup_is_done,
516 &lookups[i].async_id);
517 checkerr("ub_resolve_async", r);
523 for(i=0; i<argc; i++) {
524 fprintf(stderr, "cancel %s\n", argv[i]);
525 r = ub_cancel(ctx, lookups[i].async_id);
527 checkerr("ub_cancel", r);
532 /* wait while the hostnames are looked up. Do something useful here */
534 for(i=0; i<1000; i++) {
536 fprintf(stderr, "%g seconds passed\n", 0.1*(double)i);
538 checkerr("ub_process", r);
543 printf("timed out\n");
546 printf("lookup complete\n");
548 /* print lookup results */
549 for(i=0; i<argc; i++) {
550 print_result(&lookups[i]);
551 ub_resolve_free(lookups[i].result);