1/* 2 * Copyright (C) 2009-2012 Internet Systems Consortium, Inc. ("ISC") 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 * PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17/* $Id$ */ 18 19#include <config.h> 20 21#include <sys/types.h> 22#include <sys/socket.h> 23 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27#include <unistd.h> 28#include <netdb.h> 29 30#include <isc/app.h> 31#include <isc/buffer.h> 32#include <isc/lib.h> 33#include <isc/mem.h> 34#include <isc/socket.h> 35#include <isc/sockaddr.h> 36#include <isc/string.h> 37#include <isc/task.h> 38#include <isc/timer.h> 39#include <isc/util.h> 40 41#include <dns/client.h> 42#include <dns/fixedname.h> 43#include <dns/lib.h> 44#include <dns/message.h> 45#include <dns/name.h> 46#include <dns/rdata.h> 47#include <dns/rdataset.h> 48#include <dns/rdatastruct.h> 49#include <dns/rdatatype.h> 50#include <dns/result.h> 51 52#define MAX_PROBES 1000 53 54static dns_client_t *client = NULL; 55static isc_task_t *probe_task = NULL; 56static isc_appctx_t *actx = NULL; 57static isc_mem_t *mctx = NULL; 58static unsigned int outstanding_probes = 0; 59const char *cacheserver = "127.0.0.1"; 60static FILE *fp; 61 62typedef enum { 63 none, 64 exist, 65 nxdomain, 66 othererr, 67 multiplesoa, 68 multiplecname, 69 brokenanswer, 70 lame, 71 timedout, 72 notype, 73 unexpected 74} query_result_t; 75 76struct server { 77 ISC_LINK(struct server) link; 78 79 isc_sockaddr_t address; 80 query_result_t result_a; 81 query_result_t result_aaaa; 82}; 83 84struct probe_ns { 85 ISC_LINK(struct probe_ns) link; 86 87 dns_fixedname_t fixedname; 88 dns_name_t *name; 89 struct server *current_server; 90 ISC_LIST(struct server) servers; 91}; 92 93struct probe_trans { 94 isc_boolean_t inuse; 95 char *domain; 96 dns_fixedname_t fixedname; 97 dns_name_t *qname; 98 const char **qlabel; 99 isc_boolean_t qname_found; 100 dns_clientrestrans_t *resid; 101 dns_message_t *qmessage; 102 dns_message_t *rmessage; 103 dns_clientreqtrans_t *reqid; 104 105 /* NS list */ 106 struct probe_ns *current_ns; 107 ISC_LIST(struct probe_ns) nslist; 108}; 109 110struct lcl_stat { 111 unsigned long valid; 112 unsigned long ignore; 113 unsigned long nxdomain; 114 unsigned long othererr; 115 unsigned long multiplesoa; 116 unsigned long multiplecname; 117 unsigned long brokenanswer; 118 unsigned long lame; 119 unsigned long unknown; 120} server_stat, domain_stat; 121 122static unsigned long number_of_domains = 0; 123static unsigned long number_of_servers = 0; 124static unsigned long multiple_error_domains = 0; 125static isc_boolean_t debug_mode = ISC_FALSE; 126static int verbose_level = 0; 127static const char *qlabels[] = {"www.", "ftp.", NULL}; 128static struct probe_trans probes[MAX_PROBES]; 129 130static isc_result_t probe_domain(struct probe_trans *trans); 131static void reset_probe(struct probe_trans *trans); 132static isc_result_t fetch_nsaddress(struct probe_trans *trans); 133static isc_result_t probe_name(struct probe_trans *trans, 134 dns_rdatatype_t type); 135 136/* Dump an rdataset for debug */ 137static isc_result_t 138print_rdataset(dns_rdataset_t *rdataset, dns_name_t *owner) { 139 isc_buffer_t target; 140 isc_result_t result; 141 isc_region_t r; 142 char t[4096]; 143 144 if (!debug_mode) 145 return (ISC_R_SUCCESS); 146 147 isc_buffer_init(&target, t, sizeof(t)); 148 149 if (!dns_rdataset_isassociated(rdataset)) 150 return (ISC_R_SUCCESS); 151 result = dns_rdataset_totext(rdataset, owner, ISC_FALSE, ISC_FALSE, 152 &target); 153 if (result != ISC_R_SUCCESS) 154 return (result); 155 isc_buffer_usedregion(&target, &r); 156 printf("%.*s", (int)r.length, (char *)r.base); 157 158 return (ISC_R_SUCCESS); 159} 160 161static isc_result_t 162print_name(dns_name_t *name) { 163 isc_result_t result; 164 isc_buffer_t target; 165 isc_region_t r; 166 char t[4096]; 167 168 isc_buffer_init(&target, t, sizeof(t)); 169 result = dns_name_totext(name, ISC_TRUE, &target); 170 if (result == ISC_R_SUCCESS) { 171 isc_buffer_usedregion(&target, &r); 172 printf("%.*s", (int)r.length, (char *)r.base); 173 } else 174 printf("(invalid name)"); 175 176 return (result); 177} 178 179static isc_result_t 180print_address(FILE *fp, isc_sockaddr_t *addr) { 181 char buf[NI_MAXHOST]; 182 183 if (getnameinfo(&addr->type.sa, addr->length, buf, sizeof(buf), 184 NULL, 0, NI_NUMERICHOST) == 0) { 185 fprintf(fp, "%s", buf); 186 } else { 187 fprintf(fp, "(invalid address)"); 188 } 189 190 return (ISC_R_SUCCESS); 191} 192 193static void 194ctxs_destroy(isc_mem_t **mctxp, isc_appctx_t **actxp, 195 isc_taskmgr_t **taskmgrp, isc_socketmgr_t **socketmgrp, 196 isc_timermgr_t **timermgrp) 197{ 198 if (*taskmgrp != NULL) 199 isc_taskmgr_destroy(taskmgrp); 200 201 if (*timermgrp != NULL) 202 isc_timermgr_destroy(timermgrp); 203 204 if (*socketmgrp != NULL) 205 isc_socketmgr_destroy(socketmgrp); 206 207 if (*actxp != NULL) 208 isc_appctx_destroy(actxp); 209 210 if (*mctxp != NULL) 211 isc_mem_destroy(mctxp); 212} 213 214static isc_result_t 215ctxs_init(isc_mem_t **mctxp, isc_appctx_t **actxp, 216 isc_taskmgr_t **taskmgrp, isc_socketmgr_t **socketmgrp, 217 isc_timermgr_t **timermgrp) 218{ 219 isc_result_t result; 220 221 result = isc_mem_create(0, 0, mctxp); 222 if (result != ISC_R_SUCCESS) 223 goto fail; 224 225 result = isc_appctx_create(*mctxp, actxp); 226 if (result != ISC_R_SUCCESS) 227 goto fail; 228 229 result = isc_taskmgr_createinctx(*mctxp, *actxp, 1, 0, taskmgrp); 230 if (result != ISC_R_SUCCESS) 231 goto fail; 232 233 result = isc_socketmgr_createinctx(*mctxp, *actxp, socketmgrp); 234 if (result != ISC_R_SUCCESS) 235 goto fail; 236 237 result = isc_timermgr_createinctx(*mctxp, *actxp, timermgrp); 238 if (result != ISC_R_SUCCESS) 239 goto fail; 240 241 return (ISC_R_SUCCESS); 242 243 fail: 244 ctxs_destroy(mctxp, actxp, taskmgrp, socketmgrp, timermgrp); 245 246 return (result); 247} 248 249/* 250 * Common routine to make query data 251 */ 252static isc_result_t 253make_querymessage(dns_message_t *message, dns_name_t *qname0, 254 dns_rdatatype_t rdtype) 255{ 256 dns_name_t *qname = NULL; 257 dns_rdataset_t *qrdataset = NULL; 258 isc_result_t result; 259 260 message->opcode = dns_opcode_query; 261 message->rdclass = dns_rdataclass_in; 262 263 result = dns_message_gettempname(message, &qname); 264 if (result != ISC_R_SUCCESS) 265 goto cleanup; 266 267 result = dns_message_gettemprdataset(message, &qrdataset); 268 if (result != ISC_R_SUCCESS) 269 goto cleanup; 270 271 dns_name_init(qname, NULL); 272 dns_name_clone(qname0, qname); 273 dns_rdataset_init(qrdataset); 274 dns_rdataset_makequestion(qrdataset, message->rdclass, rdtype); 275 ISC_LIST_APPEND(qname->list, qrdataset, link); 276 dns_message_addname(message, qname, DNS_SECTION_QUESTION); 277 278 return (ISC_R_SUCCESS); 279 280 cleanup: 281 if (qname != NULL) 282 dns_message_puttempname(message, &qname); 283 if (qrdataset != NULL) 284 dns_message_puttemprdataset(message, &qrdataset); 285 if (message != NULL) 286 dns_message_destroy(&message); 287 return (result); 288} 289 290/* 291 * Update statistics 292 */ 293static inline void 294increment_entry(unsigned long *entryp) { 295 (*entryp)++; 296 INSIST(*entryp != 0); /* check overflow */ 297} 298 299static void 300update_stat(struct probe_trans *trans) { 301 struct probe_ns *pns; 302 struct server *server; 303 struct lcl_stat local_stat; 304 unsigned int err_count = 0; 305 const char *stattype; 306 307 increment_entry(&number_of_domains); 308 memset(&local_stat, 0, sizeof(local_stat)); 309 310 /* Update per sever statistics */ 311 for (pns = ISC_LIST_HEAD(trans->nslist); pns != NULL; 312 pns = ISC_LIST_NEXT(pns, link)) { 313 for (server = ISC_LIST_HEAD(pns->servers); server != NULL; 314 server = ISC_LIST_NEXT(server, link)) { 315 increment_entry(&number_of_servers); 316 317 if (server->result_aaaa == exist || 318 server->result_aaaa == notype) { 319 /* 320 * Don't care about the result of A query if 321 * the answer to AAAA query was expected. 322 */ 323 stattype = "valid"; 324 increment_entry(&server_stat.valid); 325 increment_entry(&local_stat.valid); 326 } else if (server->result_a == exist) { 327 switch (server->result_aaaa) { 328 case exist: 329 case notype: 330 stattype = "valid"; 331 increment_entry(&server_stat.valid); 332 increment_entry(&local_stat.valid); 333 break; 334 case timedout: 335 stattype = "ignore"; 336 increment_entry(&server_stat.ignore); 337 increment_entry(&local_stat.ignore); 338 break; 339 case nxdomain: 340 stattype = "nxdomain"; 341 increment_entry(&server_stat.nxdomain); 342 increment_entry(&local_stat.nxdomain); 343 break; 344 case othererr: 345 stattype = "othererr"; 346 increment_entry(&server_stat.othererr); 347 increment_entry(&local_stat.othererr); 348 break; 349 case multiplesoa: 350 stattype = "multiplesoa"; 351 increment_entry(&server_stat.multiplesoa); 352 increment_entry(&local_stat.multiplesoa); 353 break; 354 case multiplecname: 355 stattype = "multiplecname"; 356 increment_entry(&server_stat.multiplecname); 357 increment_entry(&local_stat.multiplecname); 358 break; 359 case brokenanswer: 360 stattype = "brokenanswer"; 361 increment_entry(&server_stat.brokenanswer); 362 increment_entry(&local_stat.brokenanswer); 363 break; 364 case lame: 365 stattype = "lame"; 366 increment_entry(&server_stat.lame); 367 increment_entry(&local_stat.lame); 368 break; 369 default: 370 stattype = "unknown"; 371 increment_entry(&server_stat.unknown); 372 increment_entry(&local_stat.unknown); 373 break; 374 } 375 } else { 376 stattype = "unknown"; 377 increment_entry(&server_stat.unknown); 378 increment_entry(&local_stat.unknown); 379 } 380 381 if (verbose_level > 1 || 382 (verbose_level == 1 && 383 strcmp(stattype, "valid") != 0 && 384 strcmp(stattype, "unknown") != 0)) { 385 print_name(pns->name); 386 putchar('('); 387 print_address(stdout, &server->address); 388 printf(") for %s:%s\n", trans->domain, 389 stattype); 390 } 391 } 392 } 393 394 /* Update per domain statistics */ 395 if (local_stat.ignore > 0) { 396 if (verbose_level > 0) 397 printf("%s:ignore\n", trans->domain); 398 increment_entry(&domain_stat.ignore); 399 err_count++; 400 } 401 if (local_stat.nxdomain > 0) { 402 if (verbose_level > 0) 403 printf("%s:nxdomain\n", trans->domain); 404 increment_entry(&domain_stat.nxdomain); 405 err_count++; 406 } 407 if (local_stat.othererr > 0) { 408 if (verbose_level > 0) 409 printf("%s:othererr\n", trans->domain); 410 increment_entry(&domain_stat.othererr); 411 err_count++; 412 } 413 if (local_stat.multiplesoa > 0) { 414 if (verbose_level > 0) 415 printf("%s:multiplesoa\n", trans->domain); 416 increment_entry(&domain_stat.multiplesoa); 417 err_count++; 418 } 419 if (local_stat.multiplecname > 0) { 420 if (verbose_level > 0) 421 printf("%s:multiplecname\n", trans->domain); 422 increment_entry(&domain_stat.multiplecname); 423 err_count++; 424 } 425 if (local_stat.brokenanswer > 0) { 426 if (verbose_level > 0) 427 printf("%s:brokenanswer\n", trans->domain); 428 increment_entry(&domain_stat.brokenanswer); 429 err_count++; 430 } 431 if (local_stat.lame > 0) { 432 if (verbose_level > 0) 433 printf("%s:lame\n", trans->domain); 434 increment_entry(&domain_stat.lame); 435 err_count++; 436 } 437 438 if (err_count > 1) 439 increment_entry(&multiple_error_domains); 440 441 /* 442 * We regard the domain as valid if and only if no authoritative server 443 * has a problem and at least one server is known to be valid. 444 */ 445 if (local_stat.valid > 0 && err_count == 0) { 446 if (verbose_level > 1) 447 printf("%s:valid\n", trans->domain); 448 increment_entry(&domain_stat.valid); 449 } 450 451 /* 452 * If the domain has no available server or all servers have the 453 * 'unknown' result, the domain's result is also regarded as unknown. 454 */ 455 if (local_stat.valid == 0 && err_count == 0) { 456 if (verbose_level > 1) 457 printf("%s:unknown\n", trans->domain); 458 increment_entry(&domain_stat.unknown); 459 } 460} 461 462/* 463 * Search for an existent name with an A RR 464 */ 465 466static isc_result_t 467set_nextqname(struct probe_trans *trans) { 468 isc_result_t result; 469 size_t domainlen; 470 isc_buffer_t b; 471 char buf[4096]; /* XXX ad-hoc constant, but should be enough */ 472 473 if (*trans->qlabel == NULL) 474 return (ISC_R_NOMORE); 475 476 result = isc_string_copy(buf, sizeof(buf), *trans->qlabel); 477 if (result != ISC_R_SUCCESS) 478 return (result); 479 result = isc_string_append(buf, sizeof(buf), trans->domain); 480 if (result != ISC_R_SUCCESS) 481 return (result); 482 483 domainlen = strlen(buf); 484 isc_buffer_init(&b, buf, domainlen); 485 isc_buffer_add(&b, domainlen); 486 dns_fixedname_init(&trans->fixedname); 487 trans->qname = dns_fixedname_name(&trans->fixedname); 488 result = dns_name_fromtext(trans->qname, &b, dns_rootname, 489 0, NULL); 490 491 trans->qlabel++; 492 493 return (result); 494} 495 496static void 497request_done(isc_task_t *task, isc_event_t *event) { 498 struct probe_trans *trans = event->ev_arg; 499 dns_clientreqevent_t *rev = (dns_clientreqevent_t *)event; 500 dns_message_t *rmessage; 501 struct probe_ns *pns; 502 struct server *server; 503 isc_result_t result; 504 query_result_t *resultp; 505 dns_name_t *name; 506 dns_rdataset_t *rdataset; 507 dns_rdatatype_t type; 508 509 REQUIRE(task == probe_task); 510 REQUIRE(trans != NULL && trans->inuse == ISC_TRUE); 511 rmessage = rev->rmessage; 512 REQUIRE(rmessage == trans->rmessage); 513 INSIST(outstanding_probes > 0); 514 515 server = trans->current_ns->current_server; 516 INSIST(server != NULL); 517 518 if (server->result_a == none) { 519 type = dns_rdatatype_a; 520 resultp = &server->result_a; 521 } else { 522 resultp = &server->result_aaaa; 523 type = dns_rdatatype_aaaa; 524 } 525 526 if (rev->result == ISC_R_SUCCESS) { 527 if ((rmessage->flags & DNS_MESSAGEFLAG_AA) == 0) 528 *resultp = lame; 529 else if (rmessage->rcode == dns_rcode_nxdomain) 530 *resultp = nxdomain; 531 else if (rmessage->rcode != dns_rcode_noerror) 532 *resultp = othererr; 533 else if (rmessage->counts[DNS_SECTION_ANSWER] == 0) { 534 /* no error but empty answer */ 535 *resultp = notype; 536 } else { 537 result = dns_message_firstname(rmessage, 538 DNS_SECTION_ANSWER); 539 while (result == ISC_R_SUCCESS) { 540 name = NULL; 541 dns_message_currentname(rmessage, 542 DNS_SECTION_ANSWER, 543 &name); 544 for (rdataset = ISC_LIST_HEAD(name->list); 545 rdataset != NULL; 546 rdataset = ISC_LIST_NEXT(rdataset, 547 link)) { 548 (void)print_rdataset(rdataset, name); 549 550 if (rdataset->type == 551 dns_rdatatype_cname || 552 rdataset->type == 553 dns_rdatatype_dname) { 554 /* Should chase the chain? */ 555 *resultp = exist; 556 goto found; 557 } else if (rdataset->type == type) { 558 *resultp = exist; 559 goto found; 560 } 561 } 562 result = dns_message_nextname(rmessage, 563 DNS_SECTION_ANSWER); 564 } 565 566 /* 567 * Something unexpected happened: the response 568 * contained a non-empty authoritative answer, but we 569 * could not find an expected result. 570 */ 571 *resultp = unexpected; 572 } 573 } else if (rev->result == DNS_R_RECOVERABLE || 574 rev->result == DNS_R_BADLABELTYPE) { 575 /* Broken response. Try identifying known cases. */ 576 *resultp = brokenanswer; 577 578 if (rmessage->counts[DNS_SECTION_ANSWER] > 0) { 579 result = dns_message_firstname(rmessage, 580 DNS_SECTION_ANSWER); 581 while (result == ISC_R_SUCCESS) { 582 /* 583 * Check to see if the response has multiple 584 * CNAME RRs. Update the result code if so. 585 */ 586 name = NULL; 587 dns_message_currentname(rmessage, 588 DNS_SECTION_ANSWER, 589 &name); 590 for (rdataset = ISC_LIST_HEAD(name->list); 591 rdataset != NULL; 592 rdataset = ISC_LIST_NEXT(rdataset, 593 link)) { 594 if (rdataset->type == 595 dns_rdatatype_cname && 596 dns_rdataset_count(rdataset) > 1) { 597 *resultp = multiplecname; 598 goto found; 599 } 600 } 601 result = dns_message_nextname(rmessage, 602 DNS_SECTION_ANSWER); 603 } 604 } 605 606 if (rmessage->counts[DNS_SECTION_AUTHORITY] > 0) { 607 result = dns_message_firstname(rmessage, 608 DNS_SECTION_AUTHORITY); 609 while (result == ISC_R_SUCCESS) { 610 /* 611 * Check to see if the response has multiple 612 * SOA RRs. Update the result code if so. 613 */ 614 name = NULL; 615 dns_message_currentname(rmessage, 616 DNS_SECTION_AUTHORITY, 617 &name); 618 for (rdataset = ISC_LIST_HEAD(name->list); 619 rdataset != NULL; 620 rdataset = ISC_LIST_NEXT(rdataset, 621 link)) { 622 if (rdataset->type == 623 dns_rdatatype_soa && 624 dns_rdataset_count(rdataset) > 1) { 625 *resultp = multiplesoa; 626 goto found; 627 } 628 } 629 result = dns_message_nextname(rmessage, 630 DNS_SECTION_AUTHORITY); 631 } 632 } 633 } else if (rev->result == ISC_R_TIMEDOUT) 634 *resultp = timedout; 635 else { 636 fprintf(stderr, "unexpected result: %d (domain=%s, server=", 637 rev->result, trans->domain); 638 print_address(stderr, &server->address); 639 fputc('\n', stderr); 640 *resultp = unexpected; 641 } 642 643 found: 644 INSIST(*resultp != none); 645 if (type == dns_rdatatype_a && *resultp == exist) 646 trans->qname_found = ISC_TRUE; 647 648 dns_client_destroyreqtrans(&trans->reqid); 649 isc_event_free(&event); 650 dns_message_reset(trans->rmessage, DNS_MESSAGE_INTENTPARSE); 651 652 result = probe_name(trans, type); 653 if (result == ISC_R_NOMORE) { 654 /* We've tried all addresses of all servers. */ 655 if (type == dns_rdatatype_a && trans->qname_found) { 656 /* 657 * If we've explored A RRs and found an existent 658 * record, we can move to AAAA. 659 */ 660 trans->current_ns = ISC_LIST_HEAD(trans->nslist); 661 probe_name(trans, dns_rdatatype_aaaa); 662 result = ISC_R_SUCCESS; 663 } else if (type == dns_rdatatype_a) { 664 /* 665 * No server provided an existent A RR of this name. 666 * Try next label. 667 */ 668 dns_fixedname_invalidate(&trans->fixedname); 669 trans->qname = NULL; 670 result = set_nextqname(trans); 671 if (result == ISC_R_SUCCESS) { 672 trans->current_ns = 673 ISC_LIST_HEAD(trans->nslist); 674 for (pns = trans->current_ns; pns != NULL; 675 pns = ISC_LIST_NEXT(pns, link)) { 676 for (server = ISC_LIST_HEAD(pns->servers); 677 server != NULL; 678 server = ISC_LIST_NEXT(server, 679 link)) { 680 INSIST(server->result_aaaa == 681 none); 682 server->result_a = none; 683 } 684 } 685 result = probe_name(trans, dns_rdatatype_a); 686 } 687 } 688 if (result != ISC_R_SUCCESS) { 689 /* 690 * We've explored AAAA RRs or failed to find a valid 691 * query label. Wrap up the result and move to the 692 * next domain. 693 */ 694 reset_probe(trans); 695 } 696 } else if (result != ISC_R_SUCCESS) 697 reset_probe(trans); /* XXX */ 698} 699 700static isc_result_t 701probe_name(struct probe_trans *trans, dns_rdatatype_t type) { 702 isc_result_t result; 703 struct probe_ns *pns; 704 struct server *server; 705 706 REQUIRE(trans->reqid == NULL); 707 REQUIRE(type == dns_rdatatype_a || type == dns_rdatatype_aaaa); 708 709 for (pns = trans->current_ns; pns != NULL; 710 pns = ISC_LIST_NEXT(pns, link)) { 711 for (server = ISC_LIST_HEAD(pns->servers); server != NULL; 712 server = ISC_LIST_NEXT(server, link)) { 713 if ((type == dns_rdatatype_a && 714 server->result_a == none) || 715 (type == dns_rdatatype_aaaa && 716 server->result_aaaa == none)) { 717 pns->current_server = server; 718 goto found; 719 } 720 } 721 } 722 723 found: 724 trans->current_ns = pns; 725 if (pns == NULL) 726 return (ISC_R_NOMORE); 727 728 INSIST(pns->current_server != NULL); 729 dns_message_reset(trans->qmessage, DNS_MESSAGE_INTENTRENDER); 730 result = make_querymessage(trans->qmessage, trans->qname, type); 731 if (result != ISC_R_SUCCESS) 732 return (result); 733 result = dns_client_startrequest(client, trans->qmessage, 734 trans->rmessage, 735 &pns->current_server->address, 736 0, DNS_MESSAGEPARSE_BESTEFFORT, 737 NULL, 120, 0, 4, 738 probe_task, request_done, trans, 739 &trans->reqid); 740 741 return (result); 742} 743 744/* 745 * Get IP addresses of NSes 746 */ 747 748static void 749resolve_nsaddress(isc_task_t *task, isc_event_t *event) { 750 struct probe_trans *trans = event->ev_arg; 751 dns_clientresevent_t *rev = (dns_clientresevent_t *)event; 752 dns_name_t *name; 753 dns_rdataset_t *rdataset; 754 dns_rdata_t rdata = DNS_RDATA_INIT; 755 struct probe_ns *pns = trans->current_ns; 756 isc_result_t result; 757 758 REQUIRE(task == probe_task); 759 REQUIRE(trans->inuse == ISC_TRUE); 760 REQUIRE(pns != NULL); 761 INSIST(outstanding_probes > 0); 762 763 for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL; 764 name = ISC_LIST_NEXT(name, link)) { 765 for (rdataset = ISC_LIST_HEAD(name->list); 766 rdataset != NULL; 767 rdataset = ISC_LIST_NEXT(rdataset, link)) { 768 (void)print_rdataset(rdataset, name); 769 770 if (rdataset->type != dns_rdatatype_a) 771 continue; 772 773 for (result = dns_rdataset_first(rdataset); 774 result == ISC_R_SUCCESS; 775 result = dns_rdataset_next(rdataset)) { 776 dns_rdata_in_a_t rdata_a; 777 struct server *server; 778 779 dns_rdataset_current(rdataset, &rdata); 780 result = dns_rdata_tostruct(&rdata, &rdata_a, 781 NULL); 782 if (result != ISC_R_SUCCESS) 783 continue; 784 785 server = isc_mem_get(mctx, sizeof(*server)); 786 if (server == NULL) { 787 fprintf(stderr, "resolve_nsaddress: " 788 "mem_get failed"); 789 result = ISC_R_NOMEMORY; 790 POST(result); 791 goto cleanup; 792 } 793 isc_sockaddr_fromin(&server->address, 794 &rdata_a.in_addr, 53); 795 ISC_LINK_INIT(server, link); 796 server->result_a = none; 797 server->result_aaaa = none; 798 ISC_LIST_APPEND(pns->servers, server, link); 799 } 800 } 801 } 802 803 cleanup: 804 dns_client_freeresanswer(client, &rev->answerlist); 805 dns_client_destroyrestrans(&trans->resid); 806 isc_event_free(&event); 807 808 next_ns: 809 trans->current_ns = ISC_LIST_NEXT(pns, link); 810 if (trans->current_ns == NULL) { 811 trans->current_ns = ISC_LIST_HEAD(trans->nslist); 812 dns_fixedname_invalidate(&trans->fixedname); 813 trans->qname = NULL; 814 result = set_nextqname(trans); 815 if (result == ISC_R_SUCCESS) 816 result = probe_name(trans, dns_rdatatype_a); 817 } else { 818 result = fetch_nsaddress(trans); 819 if (result != ISC_R_SUCCESS) 820 goto next_ns; /* XXX: this is unlikely to succeed */ 821 } 822 823 if (result != ISC_R_SUCCESS) 824 reset_probe(trans); 825} 826 827static isc_result_t 828fetch_nsaddress(struct probe_trans *trans) { 829 struct probe_ns *pns; 830 831 pns = trans->current_ns; 832 REQUIRE(pns != NULL); 833 834 return (dns_client_startresolve(client, pns->name, dns_rdataclass_in, 835 dns_rdatatype_a, 0, probe_task, 836 resolve_nsaddress, trans, 837 &trans->resid)); 838} 839 840/* 841 * Get NS RRset for a given domain 842 */ 843 844static void 845reset_probe(struct probe_trans *trans) { 846 struct probe_ns *pns; 847 struct server *server; 848 isc_result_t result; 849 850 REQUIRE(trans->resid == NULL); 851 REQUIRE(trans->reqid == NULL); 852 853 update_stat(trans); 854 855 dns_message_reset(trans->qmessage, DNS_MESSAGE_INTENTRENDER); 856 dns_message_reset(trans->rmessage, DNS_MESSAGE_INTENTPARSE); 857 858 trans->inuse = ISC_FALSE; 859 if (trans->domain != NULL) 860 isc_mem_free(mctx, trans->domain); 861 trans->domain = NULL; 862 if (trans->qname != NULL) 863 dns_fixedname_invalidate(&trans->fixedname); 864 trans->qname = NULL; 865 trans->qlabel = qlabels; 866 trans->qname_found = ISC_FALSE; 867 trans->current_ns = NULL; 868 869 while ((pns = ISC_LIST_HEAD(trans->nslist)) != NULL) { 870 ISC_LIST_UNLINK(trans->nslist, pns, link); 871 while ((server = ISC_LIST_HEAD(pns->servers)) != NULL) { 872 ISC_LIST_UNLINK(pns->servers, server, link); 873 isc_mem_put(mctx, server, sizeof(*server)); 874 } 875 isc_mem_put(mctx, pns, sizeof(*pns)); 876 } 877 878 outstanding_probes--; 879 880 result = probe_domain(trans); 881 if (result == ISC_R_NOMORE && outstanding_probes == 0) 882 isc_app_ctxshutdown(actx); 883} 884 885static void 886resolve_ns(isc_task_t *task, isc_event_t *event) { 887 struct probe_trans *trans = event->ev_arg; 888 dns_clientresevent_t *rev = (dns_clientresevent_t *)event; 889 dns_name_t *name; 890 dns_rdataset_t *rdataset; 891 isc_result_t result = ISC_R_SUCCESS; 892 dns_rdata_t rdata = DNS_RDATA_INIT; 893 struct probe_ns *pns; 894 895 REQUIRE(task == probe_task); 896 REQUIRE(trans->inuse == ISC_TRUE); 897 INSIST(outstanding_probes > 0); 898 899 for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL; 900 name = ISC_LIST_NEXT(name, link)) { 901 for (rdataset = ISC_LIST_HEAD(name->list); 902 rdataset != NULL; 903 rdataset = ISC_LIST_NEXT(rdataset, link)) { 904 (void)print_rdataset(rdataset, name); 905 906 if (rdataset->type != dns_rdatatype_ns) 907 continue; 908 909 for (result = dns_rdataset_first(rdataset); 910 result == ISC_R_SUCCESS; 911 result = dns_rdataset_next(rdataset)) { 912 dns_rdata_ns_t ns; 913 914 dns_rdataset_current(rdataset, &rdata); 915 /* 916 * Extract the name from the NS record. 917 */ 918 result = dns_rdata_tostruct(&rdata, &ns, NULL); 919 if (result != ISC_R_SUCCESS) 920 continue; 921 922 pns = isc_mem_get(mctx, sizeof(*pns)); 923 if (pns == NULL) { 924 fprintf(stderr, 925 "resolve_ns: mem_get failed"); 926 result = ISC_R_NOMEMORY; 927 POST(result); 928 /* 929 * XXX: should we continue with the 930 * available servers anyway? 931 */ 932 goto cleanup; 933 } 934 935 dns_fixedname_init(&pns->fixedname); 936 pns->name = 937 dns_fixedname_name(&pns->fixedname); 938 ISC_LINK_INIT(pns, link); 939 ISC_LIST_APPEND(trans->nslist, pns, link); 940 ISC_LIST_INIT(pns->servers); 941 942 dns_name_copy(&ns.name, pns->name, NULL); 943 dns_rdata_reset(&rdata); 944 dns_rdata_freestruct(&ns); 945 } 946 } 947 } 948 949 cleanup: 950 dns_client_freeresanswer(client, &rev->answerlist); 951 dns_client_destroyrestrans(&trans->resid); 952 isc_event_free(&event); 953 954 if (!ISC_LIST_EMPTY(trans->nslist)) { 955 /* Go get addresses of NSes */ 956 trans->current_ns = ISC_LIST_HEAD(trans->nslist); 957 result = fetch_nsaddress(trans); 958 } else 959 result = ISC_R_FAILURE; 960 961 if (result == ISC_R_SUCCESS) 962 return; 963 964 reset_probe(trans); 965} 966 967static isc_result_t 968probe_domain(struct probe_trans *trans) { 969 isc_result_t result; 970 size_t domainlen; 971 isc_buffer_t b; 972 char buf[4096]; /* XXX ad hoc constant, but should be enough */ 973 char *cp; 974 975 REQUIRE(trans != NULL); 976 REQUIRE(trans->inuse == ISC_FALSE); 977 REQUIRE(outstanding_probes < MAX_PROBES); 978 979 /* Construct domain */ 980 cp = fgets(buf, sizeof(buf), fp); 981 if (cp == NULL) 982 return (ISC_R_NOMORE); 983 if ((cp = strchr(buf, '\n')) != NULL) /* zap NL if any */ 984 *cp = '\0'; 985 trans->domain = isc_mem_strdup(mctx, buf); 986 if (trans->domain == NULL) { 987 fprintf(stderr, 988 "failed to allocate memory for domain: %s", cp); 989 return (ISC_R_NOMEMORY); 990 } 991 992 /* Start getting NS for the domain */ 993 domainlen = strlen(buf); 994 isc_buffer_init(&b, buf, domainlen); 995 isc_buffer_add(&b, domainlen); 996 dns_fixedname_init(&trans->fixedname); 997 trans->qname = dns_fixedname_name(&trans->fixedname); 998 result = dns_name_fromtext(trans->qname, &b, dns_rootname, 0, NULL); 999 if (result != ISC_R_SUCCESS) 1000 goto cleanup; 1001 result = dns_client_startresolve(client, trans->qname, 1002 dns_rdataclass_in, dns_rdatatype_ns, 1003 0, probe_task, resolve_ns, trans, 1004 &trans->resid); 1005 if (result != ISC_R_SUCCESS) 1006 goto cleanup; 1007 1008 trans->inuse = ISC_TRUE; 1009 outstanding_probes++; 1010 1011 return (ISC_R_SUCCESS); 1012 1013 cleanup: 1014 isc_mem_free(mctx, trans->domain); 1015 dns_fixedname_invalidate(&trans->fixedname); 1016 1017 return (result); 1018} 1019 1020ISC_PLATFORM_NORETURN_PRE static void 1021usage(void) ISC_PLATFORM_NORETURN_POST; 1022 1023static void 1024usage(void) { 1025 fprintf(stderr, "usage: nsprobe [-d] [-v [-v...]] [-c cache_address] " 1026 "[input_file]\n"); 1027 1028 exit(1); 1029} 1030 1031int 1032main(int argc, char *argv[]) { 1033 int i, ch, error; 1034 struct addrinfo hints, *res; 1035 isc_result_t result; 1036 isc_sockaddr_t sa; 1037 isc_sockaddrlist_t servers; 1038 isc_taskmgr_t *taskmgr = NULL; 1039 isc_socketmgr_t *socketmgr = NULL; 1040 isc_timermgr_t *timermgr = NULL; 1041 1042 while ((ch = getopt(argc, argv, "c:dhv")) != -1) { 1043 switch (ch) { 1044 case 'c': 1045 cacheserver = optarg; 1046 break; 1047 case 'd': 1048 debug_mode = ISC_TRUE; 1049 break; 1050 case 'h': 1051 usage(); 1052 break; 1053 case 'v': 1054 verbose_level++; 1055 break; 1056 default: 1057 usage(); 1058 break; 1059 } 1060 } 1061 1062 argc -= optind; 1063 argv += optind; 1064 1065 /* Common set up */ 1066 isc_lib_register(); 1067 result = dns_lib_init(); 1068 if (result != ISC_R_SUCCESS) { 1069 fprintf(stderr, "dns_lib_init failed: %d\n", result); 1070 exit(1); 1071 } 1072 1073 result = ctxs_init(&mctx, &actx, &taskmgr, &socketmgr, 1074 &timermgr); 1075 if (result != ISC_R_SUCCESS) { 1076 fprintf(stderr, "ctx create failed: %d\n", result); 1077 exit(1); 1078 } 1079 1080 isc_app_ctxstart(actx); 1081 1082 result = dns_client_createx(mctx, actx, taskmgr, socketmgr, 1083 timermgr, 0, &client); 1084 if (result != ISC_R_SUCCESS) { 1085 fprintf(stderr, "dns_client_createx failed: %d\n", result); 1086 exit(1); 1087 } 1088 1089 /* Set local cache server */ 1090 memset(&hints, 0, sizeof(hints)); 1091 hints.ai_family = AF_UNSPEC; 1092 hints.ai_socktype = SOCK_DGRAM; 1093 error = getaddrinfo(cacheserver, "53", &hints, &res); 1094 if (error != 0) { 1095 fprintf(stderr, "failed to convert server name (%s): %s\n", 1096 cacheserver, gai_strerror(error)); 1097 exit(1); 1098 } 1099 1100 if (res->ai_addrlen > sizeof(sa.type)) { 1101 fprintf(stderr, 1102 "assumption failure: addrlen is too long: %ld\n", 1103 (long)res->ai_addrlen); 1104 exit(1); 1105 } 1106 memcpy(&sa.type.sa, res->ai_addr, res->ai_addrlen); 1107 sa.length = res->ai_addrlen; 1108 freeaddrinfo(res); 1109 ISC_LINK_INIT(&sa, link); 1110 ISC_LIST_INIT(servers); 1111 ISC_LIST_APPEND(servers, &sa, link); 1112 result = dns_client_setservers(client, dns_rdataclass_in, NULL, 1113 &servers); 1114 if (result != ISC_R_SUCCESS) { 1115 fprintf(stderr, "failed to set server: %d\n", result); 1116 exit(1); 1117 } 1118 1119 /* Create the main task */ 1120 probe_task = NULL; 1121 result = isc_task_create(taskmgr, 0, &probe_task); 1122 if (result != ISC_R_SUCCESS) { 1123 fprintf(stderr, "failed to create task: %d\n", result); 1124 exit(1); 1125 } 1126 1127 /* Open input file */ 1128 if (argc == 0) 1129 fp = stdin; 1130 else { 1131 fp = fopen(argv[0], "r"); 1132 if (fp == NULL) { 1133 fprintf(stderr, "failed to open input file: %s\n", 1134 argv[0]); 1135 exit(1); 1136 } 1137 } 1138 1139 /* Set up and start probe */ 1140 for (i = 0; i < MAX_PROBES; i++) { 1141 probes[i].inuse = ISC_FALSE; 1142 probes[i].domain = NULL; 1143 dns_fixedname_init(&probes[i].fixedname); 1144 probes[i].qname = NULL; 1145 probes[i].qlabel = qlabels; 1146 probes[i].qname_found = ISC_FALSE; 1147 probes[i].resid = NULL; 1148 ISC_LIST_INIT(probes[i].nslist); 1149 probes[i].reqid = NULL; 1150 1151 probes[i].qmessage = NULL; 1152 result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, 1153 &probes[i].qmessage); 1154 if (result == ISC_R_SUCCESS) { 1155 result = dns_message_create(mctx, 1156 DNS_MESSAGE_INTENTPARSE, 1157 &probes[i].rmessage); 1158 } 1159 if (result != ISC_R_SUCCESS) { 1160 fprintf(stderr, "initialization failure\n"); 1161 exit(1); 1162 } 1163 } 1164 for (i = 0; i < MAX_PROBES; i++) { 1165 result = probe_domain(&probes[i]); 1166 if (result == ISC_R_NOMORE) 1167 break; 1168 else if (result != ISC_R_SUCCESS) { 1169 fprintf(stderr, "failed to issue an initial probe\n"); 1170 exit(1); 1171 } 1172 } 1173 1174 /* Start event loop */ 1175 isc_app_ctxrun(actx); 1176 1177 /* Dump results */ 1178 printf("Per domain results (out of %lu domains):\n", 1179 number_of_domains); 1180 printf(" valid: %lu\n" 1181 " ignore: %lu\n" 1182 " nxdomain: %lu\n" 1183 " othererr: %lu\n" 1184 " multiplesoa: %lu\n" 1185 " multiplecname: %lu\n" 1186 " brokenanswer: %lu\n" 1187 " lame: %lu\n" 1188 " unknown: %lu\n" 1189 " multiple errors: %lu\n", 1190 domain_stat.valid, domain_stat.ignore, domain_stat.nxdomain, 1191 domain_stat.othererr, domain_stat.multiplesoa, 1192 domain_stat.multiplecname, domain_stat.brokenanswer, 1193 domain_stat.lame, domain_stat.unknown, multiple_error_domains); 1194 printf("Per server results (out of %lu servers):\n", 1195 number_of_servers); 1196 printf(" valid: %lu\n" 1197 " ignore: %lu\n" 1198 " nxdomain: %lu\n" 1199 " othererr: %lu\n" 1200 " multiplesoa: %lu\n" 1201 " multiplecname: %lu\n" 1202 " brokenanswer: %lu\n" 1203 " lame: %lu\n" 1204 " unknown: %lu\n", 1205 server_stat.valid, server_stat.ignore, server_stat.nxdomain, 1206 server_stat.othererr, server_stat.multiplesoa, 1207 server_stat.multiplecname, server_stat.brokenanswer, 1208 server_stat.lame, server_stat.unknown); 1209 1210 /* Cleanup */ 1211 for (i = 0; i < MAX_PROBES; i++) { 1212 dns_message_destroy(&probes[i].qmessage); 1213 dns_message_destroy(&probes[i].rmessage); 1214 } 1215 isc_task_detach(&probe_task); 1216 dns_client_destroy(&client); 1217 dns_lib_shutdown(); 1218 isc_app_ctxfinish(actx); 1219 ctxs_destroy(&mctx, &actx, &taskmgr, &socketmgr, &timermgr); 1220 1221 exit(0); 1222} 1223