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