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 /* init log now because solaris thr_key_create() is not threadsafe */ 410 log_init(0,0,0); 411 /* lock debug start (if any) */ 412 checklock_start(); 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, NULL); 492 #endif 493 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) 494 (void)SSL_library_init(); 495 #else 496 (void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); 497 #endif 498 #endif /* HAVE_SSL */ 499 500 if(ext) 501 return ext_test(ctx, argc, argv); 502 503 /* allocate array for results. */ 504 lookups = (struct lookinfo*)calloc((size_t)argc, 505 sizeof(struct lookinfo)); 506 if(!lookups) { 507 printf("out of memory\n"); 508 return 1; 509 } 510 511 /* perform asynchronous calls */ 512 num_wait = argc; 513 for(i=0; i<argc; i++) { 514 lookups[i].name = argv[i]; 515 if(blocking) { 516 fprintf(stderr, "lookup %s\n", argv[i]); 517 r = ub_resolve(ctx, argv[i], LDNS_RR_TYPE_A, 518 LDNS_RR_CLASS_IN, &lookups[i].result); 519 checkerr("ub_resolve", r); 520 } else { 521 fprintf(stderr, "start async lookup %s\n", argv[i]); 522 r = ub_resolve_async(ctx, argv[i], LDNS_RR_TYPE_A, 523 LDNS_RR_CLASS_IN, &lookups[i], &lookup_is_done, 524 &lookups[i].async_id); 525 checkerr("ub_resolve_async", r); 526 } 527 } 528 if(blocking) 529 num_wait = 0; 530 else if(cancel) { 531 for(i=0; i<argc; i++) { 532 fprintf(stderr, "cancel %s\n", argv[i]); 533 r = ub_cancel(ctx, lookups[i].async_id); 534 if(r != UB_NOID) 535 checkerr("ub_cancel", r); 536 } 537 num_wait = 0; 538 } 539 540 /* wait while the hostnames are looked up. Do something useful here */ 541 if(num_wait > 0) 542 for(i=0; i<1000; i++) { 543 usleep(100000); 544 fprintf(stderr, "%g seconds passed\n", 0.1*(double)i); 545 r = ub_process(ctx); 546 checkerr("ub_process", r); 547 if(num_wait == 0) 548 break; 549 } 550 if(i>=999) { 551 printf("timed out\n"); 552 return 0; 553 } 554 printf("lookup complete\n"); 555 556 /* print lookup results */ 557 for(i=0; i<argc; i++) { 558 print_result(&lookups[i]); 559 ub_resolve_free(lookups[i].result); 560 } 561 562 ub_ctx_delete(ctx); 563 free(lookups); 564 checklock_stop(); 565 return 0; 566 } 567