1224090Sdougb/* 2262706Serwin * Copyright (C) 2009-2014 Internet Systems Consortium, Inc. ("ISC") 3224090Sdougb * 4224090Sdougb * Permission to use, copy, modify, and/or distribute this software for any 5224090Sdougb * purpose with or without fee is hereby granted, provided that the above 6224090Sdougb * copyright notice and this permission notice appear in all copies. 7224090Sdougb * 8224090Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9224090Sdougb * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10224090Sdougb * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11224090Sdougb * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12224090Sdougb * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13224090Sdougb * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14224090Sdougb * PERFORMANCE OF THIS SOFTWARE. 15224090Sdougb */ 16224090Sdougb 17234010Sdougb/* $Id$ */ 18224090Sdougb 19224090Sdougb#include <config.h> 20224090Sdougb 21224090Sdougb#include <sys/types.h> 22224090Sdougb#include <sys/socket.h> 23224090Sdougb 24224090Sdougb#include <stdio.h> 25224090Sdougb#include <stdlib.h> 26224090Sdougb#include <string.h> 27224090Sdougb#include <unistd.h> 28224090Sdougb#include <netdb.h> 29224090Sdougb 30224090Sdougb#include <isc/app.h> 31224090Sdougb#include <isc/buffer.h> 32224090Sdougb#include <isc/lib.h> 33224090Sdougb#include <isc/mem.h> 34224090Sdougb#include <isc/socket.h> 35224090Sdougb#include <isc/sockaddr.h> 36224090Sdougb#include <isc/string.h> 37224090Sdougb#include <isc/task.h> 38224090Sdougb#include <isc/timer.h> 39224090Sdougb#include <isc/util.h> 40224090Sdougb 41224090Sdougb#include <dns/client.h> 42224090Sdougb#include <dns/fixedname.h> 43224090Sdougb#include <dns/lib.h> 44224090Sdougb#include <dns/message.h> 45224090Sdougb#include <dns/name.h> 46224090Sdougb#include <dns/rdata.h> 47224090Sdougb#include <dns/rdataset.h> 48224090Sdougb#include <dns/rdatastruct.h> 49224090Sdougb#include <dns/rdatatype.h> 50224090Sdougb#include <dns/result.h> 51224090Sdougb 52224090Sdougb#define MAX_PROBES 1000 53224090Sdougb 54224090Sdougbstatic dns_client_t *client = NULL; 55224090Sdougbstatic isc_task_t *probe_task = NULL; 56224090Sdougbstatic isc_appctx_t *actx = NULL; 57224090Sdougbstatic isc_mem_t *mctx = NULL; 58224090Sdougbstatic unsigned int outstanding_probes = 0; 59224090Sdougbconst char *cacheserver = "127.0.0.1"; 60224090Sdougbstatic FILE *fp; 61224090Sdougb 62224090Sdougbtypedef enum { 63224090Sdougb none, 64224090Sdougb exist, 65224090Sdougb nxdomain, 66224090Sdougb othererr, 67224090Sdougb multiplesoa, 68224090Sdougb multiplecname, 69224090Sdougb brokenanswer, 70224090Sdougb lame, 71224090Sdougb timedout, 72224090Sdougb notype, 73224090Sdougb unexpected 74224090Sdougb} query_result_t; 75224090Sdougb 76224090Sdougbstruct server { 77224090Sdougb ISC_LINK(struct server) link; 78224090Sdougb 79224090Sdougb isc_sockaddr_t address; 80224090Sdougb query_result_t result_a; 81224090Sdougb query_result_t result_aaaa; 82224090Sdougb}; 83224090Sdougb 84224090Sdougbstruct probe_ns { 85224090Sdougb ISC_LINK(struct probe_ns) link; 86224090Sdougb 87224090Sdougb dns_fixedname_t fixedname; 88224090Sdougb dns_name_t *name; 89224090Sdougb struct server *current_server; 90224090Sdougb ISC_LIST(struct server) servers; 91224090Sdougb}; 92224090Sdougb 93224090Sdougbstruct probe_trans { 94224090Sdougb isc_boolean_t inuse; 95224090Sdougb char *domain; 96224090Sdougb dns_fixedname_t fixedname; 97224090Sdougb dns_name_t *qname; 98224090Sdougb const char **qlabel; 99224090Sdougb isc_boolean_t qname_found; 100224090Sdougb dns_clientrestrans_t *resid; 101224090Sdougb dns_message_t *qmessage; 102224090Sdougb dns_message_t *rmessage; 103224090Sdougb dns_clientreqtrans_t *reqid; 104224090Sdougb 105224090Sdougb /* NS list */ 106224090Sdougb struct probe_ns *current_ns; 107224090Sdougb ISC_LIST(struct probe_ns) nslist; 108224090Sdougb}; 109224090Sdougb 110224090Sdougbstruct lcl_stat { 111224090Sdougb unsigned long valid; 112224090Sdougb unsigned long ignore; 113224090Sdougb unsigned long nxdomain; 114224090Sdougb unsigned long othererr; 115224090Sdougb unsigned long multiplesoa; 116224090Sdougb unsigned long multiplecname; 117224090Sdougb unsigned long brokenanswer; 118224090Sdougb unsigned long lame; 119224090Sdougb unsigned long unknown; 120224090Sdougb} server_stat, domain_stat; 121224090Sdougb 122224090Sdougbstatic unsigned long number_of_domains = 0; 123224090Sdougbstatic unsigned long number_of_servers = 0; 124224090Sdougbstatic unsigned long multiple_error_domains = 0; 125224090Sdougbstatic isc_boolean_t debug_mode = ISC_FALSE; 126224090Sdougbstatic int verbose_level = 0; 127224090Sdougbstatic const char *qlabels[] = {"www.", "ftp.", NULL}; 128224090Sdougbstatic struct probe_trans probes[MAX_PROBES]; 129224090Sdougb 130224090Sdougbstatic isc_result_t probe_domain(struct probe_trans *trans); 131224090Sdougbstatic void reset_probe(struct probe_trans *trans); 132224090Sdougbstatic isc_result_t fetch_nsaddress(struct probe_trans *trans); 133224090Sdougbstatic isc_result_t probe_name(struct probe_trans *trans, 134224090Sdougb dns_rdatatype_t type); 135224090Sdougb 136224090Sdougb/* Dump an rdataset for debug */ 137224090Sdougbstatic isc_result_t 138224090Sdougbprint_rdataset(dns_rdataset_t *rdataset, dns_name_t *owner) { 139224090Sdougb isc_buffer_t target; 140224090Sdougb isc_result_t result; 141224090Sdougb isc_region_t r; 142224090Sdougb char t[4096]; 143224090Sdougb 144224090Sdougb if (!debug_mode) 145224090Sdougb return (ISC_R_SUCCESS); 146224090Sdougb 147224090Sdougb isc_buffer_init(&target, t, sizeof(t)); 148224090Sdougb 149224090Sdougb if (!dns_rdataset_isassociated(rdataset)) 150224090Sdougb return (ISC_R_SUCCESS); 151224090Sdougb result = dns_rdataset_totext(rdataset, owner, ISC_FALSE, ISC_FALSE, 152224090Sdougb &target); 153224090Sdougb if (result != ISC_R_SUCCESS) 154224090Sdougb return (result); 155224090Sdougb isc_buffer_usedregion(&target, &r); 156224090Sdougb printf("%.*s", (int)r.length, (char *)r.base); 157224090Sdougb 158224090Sdougb return (ISC_R_SUCCESS); 159224090Sdougb} 160224090Sdougb 161224090Sdougbstatic isc_result_t 162224090Sdougbprint_name(dns_name_t *name) { 163224090Sdougb isc_result_t result; 164224090Sdougb isc_buffer_t target; 165224090Sdougb isc_region_t r; 166224090Sdougb char t[4096]; 167224090Sdougb 168224090Sdougb isc_buffer_init(&target, t, sizeof(t)); 169224090Sdougb result = dns_name_totext(name, ISC_TRUE, &target); 170224090Sdougb if (result == ISC_R_SUCCESS) { 171224090Sdougb isc_buffer_usedregion(&target, &r); 172224090Sdougb printf("%.*s", (int)r.length, (char *)r.base); 173224090Sdougb } else 174224090Sdougb printf("(invalid name)"); 175224090Sdougb 176224090Sdougb return (result); 177224090Sdougb} 178224090Sdougb 179224090Sdougbstatic isc_result_t 180224090Sdougbprint_address(FILE *fp, isc_sockaddr_t *addr) { 181224090Sdougb char buf[NI_MAXHOST]; 182224090Sdougb 183224090Sdougb if (getnameinfo(&addr->type.sa, addr->length, buf, sizeof(buf), 184224090Sdougb NULL, 0, NI_NUMERICHOST) == 0) { 185224090Sdougb fprintf(fp, "%s", buf); 186224090Sdougb } else { 187224090Sdougb fprintf(fp, "(invalid address)"); 188224090Sdougb } 189224090Sdougb 190224090Sdougb return (ISC_R_SUCCESS); 191224090Sdougb} 192224090Sdougb 193224090Sdougbstatic void 194224090Sdougbctxs_destroy(isc_mem_t **mctxp, isc_appctx_t **actxp, 195224090Sdougb isc_taskmgr_t **taskmgrp, isc_socketmgr_t **socketmgrp, 196224090Sdougb isc_timermgr_t **timermgrp) 197224090Sdougb{ 198224090Sdougb if (*taskmgrp != NULL) 199224090Sdougb isc_taskmgr_destroy(taskmgrp); 200224090Sdougb 201224090Sdougb if (*timermgrp != NULL) 202224090Sdougb isc_timermgr_destroy(timermgrp); 203224090Sdougb 204224090Sdougb if (*socketmgrp != NULL) 205224090Sdougb isc_socketmgr_destroy(socketmgrp); 206224090Sdougb 207224090Sdougb if (*actxp != NULL) 208224090Sdougb isc_appctx_destroy(actxp); 209224090Sdougb 210224090Sdougb if (*mctxp != NULL) 211224090Sdougb isc_mem_destroy(mctxp); 212224090Sdougb} 213224090Sdougb 214224090Sdougbstatic isc_result_t 215224090Sdougbctxs_init(isc_mem_t **mctxp, isc_appctx_t **actxp, 216224090Sdougb isc_taskmgr_t **taskmgrp, isc_socketmgr_t **socketmgrp, 217224090Sdougb isc_timermgr_t **timermgrp) 218224090Sdougb{ 219224090Sdougb isc_result_t result; 220224090Sdougb 221224090Sdougb result = isc_mem_create(0, 0, mctxp); 222224090Sdougb if (result != ISC_R_SUCCESS) 223224090Sdougb goto fail; 224224090Sdougb 225224090Sdougb result = isc_appctx_create(*mctxp, actxp); 226224090Sdougb if (result != ISC_R_SUCCESS) 227224090Sdougb goto fail; 228224090Sdougb 229224090Sdougb result = isc_taskmgr_createinctx(*mctxp, *actxp, 1, 0, taskmgrp); 230224090Sdougb if (result != ISC_R_SUCCESS) 231224090Sdougb goto fail; 232224090Sdougb 233224090Sdougb result = isc_socketmgr_createinctx(*mctxp, *actxp, socketmgrp); 234224090Sdougb if (result != ISC_R_SUCCESS) 235224090Sdougb goto fail; 236224090Sdougb 237224090Sdougb result = isc_timermgr_createinctx(*mctxp, *actxp, timermgrp); 238224090Sdougb if (result != ISC_R_SUCCESS) 239224090Sdougb goto fail; 240224090Sdougb 241224090Sdougb return (ISC_R_SUCCESS); 242224090Sdougb 243224090Sdougb fail: 244224090Sdougb ctxs_destroy(mctxp, actxp, taskmgrp, socketmgrp, timermgrp); 245224090Sdougb 246224090Sdougb return (result); 247224090Sdougb} 248224090Sdougb 249224090Sdougb/* 250224090Sdougb * Common routine to make query data 251224090Sdougb */ 252224090Sdougbstatic isc_result_t 253224090Sdougbmake_querymessage(dns_message_t *message, dns_name_t *qname0, 254224090Sdougb dns_rdatatype_t rdtype) 255224090Sdougb{ 256224090Sdougb dns_name_t *qname = NULL; 257224090Sdougb dns_rdataset_t *qrdataset = NULL; 258224090Sdougb isc_result_t result; 259224090Sdougb 260224090Sdougb message->opcode = dns_opcode_query; 261224090Sdougb message->rdclass = dns_rdataclass_in; 262224090Sdougb 263224090Sdougb result = dns_message_gettempname(message, &qname); 264224090Sdougb if (result != ISC_R_SUCCESS) 265224090Sdougb goto cleanup; 266224090Sdougb 267224090Sdougb result = dns_message_gettemprdataset(message, &qrdataset); 268224090Sdougb if (result != ISC_R_SUCCESS) 269224090Sdougb goto cleanup; 270224090Sdougb 271224090Sdougb dns_name_init(qname, NULL); 272224090Sdougb dns_name_clone(qname0, qname); 273224090Sdougb dns_rdataset_init(qrdataset); 274224090Sdougb dns_rdataset_makequestion(qrdataset, message->rdclass, rdtype); 275224090Sdougb ISC_LIST_APPEND(qname->list, qrdataset, link); 276224090Sdougb dns_message_addname(message, qname, DNS_SECTION_QUESTION); 277224090Sdougb 278224090Sdougb return (ISC_R_SUCCESS); 279224090Sdougb 280224090Sdougb cleanup: 281224090Sdougb if (qname != NULL) 282224090Sdougb dns_message_puttempname(message, &qname); 283224090Sdougb if (qrdataset != NULL) 284224090Sdougb dns_message_puttemprdataset(message, &qrdataset); 285224090Sdougb return (result); 286224090Sdougb} 287224090Sdougb 288224090Sdougb/* 289224090Sdougb * Update statistics 290224090Sdougb */ 291224090Sdougbstatic inline void 292224090Sdougbincrement_entry(unsigned long *entryp) { 293224090Sdougb (*entryp)++; 294254402Serwin INSIST(*entryp != 0U); /* check overflow */ 295224090Sdougb} 296224090Sdougb 297224090Sdougbstatic void 298224090Sdougbupdate_stat(struct probe_trans *trans) { 299224090Sdougb struct probe_ns *pns; 300224090Sdougb struct server *server; 301224090Sdougb struct lcl_stat local_stat; 302224090Sdougb unsigned int err_count = 0; 303224090Sdougb const char *stattype; 304224090Sdougb 305224090Sdougb increment_entry(&number_of_domains); 306224090Sdougb memset(&local_stat, 0, sizeof(local_stat)); 307224090Sdougb 308224090Sdougb /* Update per sever statistics */ 309224090Sdougb for (pns = ISC_LIST_HEAD(trans->nslist); pns != NULL; 310224090Sdougb pns = ISC_LIST_NEXT(pns, link)) { 311224090Sdougb for (server = ISC_LIST_HEAD(pns->servers); server != NULL; 312224090Sdougb server = ISC_LIST_NEXT(server, link)) { 313224090Sdougb increment_entry(&number_of_servers); 314224090Sdougb 315224090Sdougb if (server->result_aaaa == exist || 316224090Sdougb server->result_aaaa == notype) { 317224090Sdougb /* 318224090Sdougb * Don't care about the result of A query if 319224090Sdougb * the answer to AAAA query was expected. 320224090Sdougb */ 321224090Sdougb stattype = "valid"; 322224090Sdougb increment_entry(&server_stat.valid); 323224090Sdougb increment_entry(&local_stat.valid); 324224090Sdougb } else if (server->result_a == exist) { 325224090Sdougb switch (server->result_aaaa) { 326224090Sdougb case exist: 327224090Sdougb case notype: 328224090Sdougb stattype = "valid"; 329224090Sdougb increment_entry(&server_stat.valid); 330224090Sdougb increment_entry(&local_stat.valid); 331224090Sdougb break; 332224090Sdougb case timedout: 333224090Sdougb stattype = "ignore"; 334224090Sdougb increment_entry(&server_stat.ignore); 335224090Sdougb increment_entry(&local_stat.ignore); 336224090Sdougb break; 337224090Sdougb case nxdomain: 338224090Sdougb stattype = "nxdomain"; 339224090Sdougb increment_entry(&server_stat.nxdomain); 340224090Sdougb increment_entry(&local_stat.nxdomain); 341224090Sdougb break; 342224090Sdougb case othererr: 343224090Sdougb stattype = "othererr"; 344224090Sdougb increment_entry(&server_stat.othererr); 345224090Sdougb increment_entry(&local_stat.othererr); 346224090Sdougb break; 347224090Sdougb case multiplesoa: 348224090Sdougb stattype = "multiplesoa"; 349224090Sdougb increment_entry(&server_stat.multiplesoa); 350224090Sdougb increment_entry(&local_stat.multiplesoa); 351224090Sdougb break; 352224090Sdougb case multiplecname: 353224090Sdougb stattype = "multiplecname"; 354224090Sdougb increment_entry(&server_stat.multiplecname); 355224090Sdougb increment_entry(&local_stat.multiplecname); 356224090Sdougb break; 357224090Sdougb case brokenanswer: 358224090Sdougb stattype = "brokenanswer"; 359224090Sdougb increment_entry(&server_stat.brokenanswer); 360224090Sdougb increment_entry(&local_stat.brokenanswer); 361224090Sdougb break; 362224090Sdougb case lame: 363224090Sdougb stattype = "lame"; 364224090Sdougb increment_entry(&server_stat.lame); 365224090Sdougb increment_entry(&local_stat.lame); 366224090Sdougb break; 367224090Sdougb default: 368224090Sdougb stattype = "unknown"; 369224090Sdougb increment_entry(&server_stat.unknown); 370224090Sdougb increment_entry(&local_stat.unknown); 371224090Sdougb break; 372224090Sdougb } 373224090Sdougb } else { 374224090Sdougb stattype = "unknown"; 375224090Sdougb increment_entry(&server_stat.unknown); 376224090Sdougb increment_entry(&local_stat.unknown); 377224090Sdougb } 378224090Sdougb 379224090Sdougb if (verbose_level > 1 || 380224090Sdougb (verbose_level == 1 && 381224090Sdougb strcmp(stattype, "valid") != 0 && 382224090Sdougb strcmp(stattype, "unknown") != 0)) { 383224090Sdougb print_name(pns->name); 384224090Sdougb putchar('('); 385224090Sdougb print_address(stdout, &server->address); 386224090Sdougb printf(") for %s:%s\n", trans->domain, 387224090Sdougb stattype); 388224090Sdougb } 389224090Sdougb } 390224090Sdougb } 391224090Sdougb 392224090Sdougb /* Update per domain statistics */ 393254402Serwin if (local_stat.ignore > 0U) { 394224090Sdougb if (verbose_level > 0) 395224090Sdougb printf("%s:ignore\n", trans->domain); 396224090Sdougb increment_entry(&domain_stat.ignore); 397224090Sdougb err_count++; 398224090Sdougb } 399254402Serwin if (local_stat.nxdomain > 0U) { 400224090Sdougb if (verbose_level > 0) 401224090Sdougb printf("%s:nxdomain\n", trans->domain); 402224090Sdougb increment_entry(&domain_stat.nxdomain); 403224090Sdougb err_count++; 404224090Sdougb } 405254402Serwin if (local_stat.othererr > 0U) { 406224090Sdougb if (verbose_level > 0) 407224090Sdougb printf("%s:othererr\n", trans->domain); 408224090Sdougb increment_entry(&domain_stat.othererr); 409224090Sdougb err_count++; 410224090Sdougb } 411254402Serwin if (local_stat.multiplesoa > 0U) { 412224090Sdougb if (verbose_level > 0) 413224090Sdougb printf("%s:multiplesoa\n", trans->domain); 414224090Sdougb increment_entry(&domain_stat.multiplesoa); 415224090Sdougb err_count++; 416224090Sdougb } 417254402Serwin if (local_stat.multiplecname > 0U) { 418224090Sdougb if (verbose_level > 0) 419224090Sdougb printf("%s:multiplecname\n", trans->domain); 420224090Sdougb increment_entry(&domain_stat.multiplecname); 421224090Sdougb err_count++; 422224090Sdougb } 423254402Serwin if (local_stat.brokenanswer > 0U) { 424224090Sdougb if (verbose_level > 0) 425224090Sdougb printf("%s:brokenanswer\n", trans->domain); 426224090Sdougb increment_entry(&domain_stat.brokenanswer); 427224090Sdougb err_count++; 428224090Sdougb } 429254402Serwin if (local_stat.lame > 0U) { 430224090Sdougb if (verbose_level > 0) 431224090Sdougb printf("%s:lame\n", trans->domain); 432224090Sdougb increment_entry(&domain_stat.lame); 433224090Sdougb err_count++; 434224090Sdougb } 435224090Sdougb 436254402Serwin if (err_count > 1U) 437224090Sdougb increment_entry(&multiple_error_domains); 438224090Sdougb 439224090Sdougb /* 440224090Sdougb * We regard the domain as valid if and only if no authoritative server 441224090Sdougb * has a problem and at least one server is known to be valid. 442224090Sdougb */ 443254402Serwin if (local_stat.valid > 0U && err_count == 0U) { 444224090Sdougb if (verbose_level > 1) 445224090Sdougb printf("%s:valid\n", trans->domain); 446224090Sdougb increment_entry(&domain_stat.valid); 447224090Sdougb } 448224090Sdougb 449224090Sdougb /* 450224090Sdougb * If the domain has no available server or all servers have the 451224090Sdougb * 'unknown' result, the domain's result is also regarded as unknown. 452224090Sdougb */ 453254402Serwin if (local_stat.valid == 0U && err_count == 0U) { 454224090Sdougb if (verbose_level > 1) 455224090Sdougb printf("%s:unknown\n", trans->domain); 456224090Sdougb increment_entry(&domain_stat.unknown); 457224090Sdougb } 458224090Sdougb} 459224090Sdougb 460224090Sdougb/* 461224090Sdougb * Search for an existent name with an A RR 462224090Sdougb */ 463224090Sdougb 464224090Sdougbstatic isc_result_t 465224090Sdougbset_nextqname(struct probe_trans *trans) { 466224090Sdougb isc_result_t result; 467224090Sdougb size_t domainlen; 468224090Sdougb isc_buffer_t b; 469224090Sdougb char buf[4096]; /* XXX ad-hoc constant, but should be enough */ 470224090Sdougb 471224090Sdougb if (*trans->qlabel == NULL) 472224090Sdougb return (ISC_R_NOMORE); 473224090Sdougb 474224090Sdougb result = isc_string_copy(buf, sizeof(buf), *trans->qlabel); 475224090Sdougb if (result != ISC_R_SUCCESS) 476224090Sdougb return (result); 477224090Sdougb result = isc_string_append(buf, sizeof(buf), trans->domain); 478224090Sdougb if (result != ISC_R_SUCCESS) 479224090Sdougb return (result); 480224090Sdougb 481224090Sdougb domainlen = strlen(buf); 482224090Sdougb isc_buffer_init(&b, buf, domainlen); 483224090Sdougb isc_buffer_add(&b, domainlen); 484224090Sdougb dns_fixedname_init(&trans->fixedname); 485224090Sdougb trans->qname = dns_fixedname_name(&trans->fixedname); 486224090Sdougb result = dns_name_fromtext(trans->qname, &b, dns_rootname, 487224090Sdougb 0, NULL); 488224090Sdougb 489224090Sdougb trans->qlabel++; 490224090Sdougb 491224090Sdougb return (result); 492224090Sdougb} 493224090Sdougb 494224090Sdougbstatic void 495224090Sdougbrequest_done(isc_task_t *task, isc_event_t *event) { 496224090Sdougb struct probe_trans *trans = event->ev_arg; 497224090Sdougb dns_clientreqevent_t *rev = (dns_clientreqevent_t *)event; 498224090Sdougb dns_message_t *rmessage; 499224090Sdougb struct probe_ns *pns; 500224090Sdougb struct server *server; 501224090Sdougb isc_result_t result; 502224090Sdougb query_result_t *resultp; 503224090Sdougb dns_name_t *name; 504224090Sdougb dns_rdataset_t *rdataset; 505224090Sdougb dns_rdatatype_t type; 506224090Sdougb 507224090Sdougb REQUIRE(task == probe_task); 508224090Sdougb REQUIRE(trans != NULL && trans->inuse == ISC_TRUE); 509224090Sdougb rmessage = rev->rmessage; 510224090Sdougb REQUIRE(rmessage == trans->rmessage); 511224090Sdougb INSIST(outstanding_probes > 0); 512224090Sdougb 513224090Sdougb server = trans->current_ns->current_server; 514224090Sdougb INSIST(server != NULL); 515224090Sdougb 516224090Sdougb if (server->result_a == none) { 517224090Sdougb type = dns_rdatatype_a; 518224090Sdougb resultp = &server->result_a; 519224090Sdougb } else { 520224090Sdougb resultp = &server->result_aaaa; 521224090Sdougb type = dns_rdatatype_aaaa; 522224090Sdougb } 523224090Sdougb 524224090Sdougb if (rev->result == ISC_R_SUCCESS) { 525224090Sdougb if ((rmessage->flags & DNS_MESSAGEFLAG_AA) == 0) 526224090Sdougb *resultp = lame; 527224090Sdougb else if (rmessage->rcode == dns_rcode_nxdomain) 528224090Sdougb *resultp = nxdomain; 529224090Sdougb else if (rmessage->rcode != dns_rcode_noerror) 530224090Sdougb *resultp = othererr; 531224090Sdougb else if (rmessage->counts[DNS_SECTION_ANSWER] == 0) { 532224090Sdougb /* no error but empty answer */ 533224090Sdougb *resultp = notype; 534224090Sdougb } else { 535224090Sdougb result = dns_message_firstname(rmessage, 536224090Sdougb DNS_SECTION_ANSWER); 537224090Sdougb while (result == ISC_R_SUCCESS) { 538224090Sdougb name = NULL; 539224090Sdougb dns_message_currentname(rmessage, 540224090Sdougb DNS_SECTION_ANSWER, 541224090Sdougb &name); 542224090Sdougb for (rdataset = ISC_LIST_HEAD(name->list); 543224090Sdougb rdataset != NULL; 544224090Sdougb rdataset = ISC_LIST_NEXT(rdataset, 545224090Sdougb link)) { 546224090Sdougb (void)print_rdataset(rdataset, name); 547224090Sdougb 548224090Sdougb if (rdataset->type == 549224090Sdougb dns_rdatatype_cname || 550224090Sdougb rdataset->type == 551224090Sdougb dns_rdatatype_dname) { 552224090Sdougb /* Should chase the chain? */ 553224090Sdougb *resultp = exist; 554224090Sdougb goto found; 555224090Sdougb } else if (rdataset->type == type) { 556224090Sdougb *resultp = exist; 557224090Sdougb goto found; 558224090Sdougb } 559224090Sdougb } 560224090Sdougb result = dns_message_nextname(rmessage, 561224090Sdougb DNS_SECTION_ANSWER); 562224090Sdougb } 563224090Sdougb 564224090Sdougb /* 565224090Sdougb * Something unexpected happened: the response 566224090Sdougb * contained a non-empty authoritative answer, but we 567224090Sdougb * could not find an expected result. 568224090Sdougb */ 569224090Sdougb *resultp = unexpected; 570224090Sdougb } 571224090Sdougb } else if (rev->result == DNS_R_RECOVERABLE || 572224090Sdougb rev->result == DNS_R_BADLABELTYPE) { 573224090Sdougb /* Broken response. Try identifying known cases. */ 574224090Sdougb *resultp = brokenanswer; 575224090Sdougb 576224090Sdougb if (rmessage->counts[DNS_SECTION_ANSWER] > 0) { 577224090Sdougb result = dns_message_firstname(rmessage, 578224090Sdougb DNS_SECTION_ANSWER); 579224090Sdougb while (result == ISC_R_SUCCESS) { 580224090Sdougb /* 581224090Sdougb * Check to see if the response has multiple 582224090Sdougb * CNAME RRs. Update the result code if so. 583224090Sdougb */ 584224090Sdougb name = NULL; 585224090Sdougb dns_message_currentname(rmessage, 586224090Sdougb DNS_SECTION_ANSWER, 587224090Sdougb &name); 588224090Sdougb for (rdataset = ISC_LIST_HEAD(name->list); 589224090Sdougb rdataset != NULL; 590224090Sdougb rdataset = ISC_LIST_NEXT(rdataset, 591224090Sdougb link)) { 592224090Sdougb if (rdataset->type == 593224090Sdougb dns_rdatatype_cname && 594224090Sdougb dns_rdataset_count(rdataset) > 1) { 595224090Sdougb *resultp = multiplecname; 596224090Sdougb goto found; 597224090Sdougb } 598224090Sdougb } 599224090Sdougb result = dns_message_nextname(rmessage, 600224090Sdougb DNS_SECTION_ANSWER); 601224090Sdougb } 602224090Sdougb } 603224090Sdougb 604224090Sdougb if (rmessage->counts[DNS_SECTION_AUTHORITY] > 0) { 605224090Sdougb result = dns_message_firstname(rmessage, 606224090Sdougb DNS_SECTION_AUTHORITY); 607224090Sdougb while (result == ISC_R_SUCCESS) { 608224090Sdougb /* 609224090Sdougb * Check to see if the response has multiple 610224090Sdougb * SOA RRs. Update the result code if so. 611224090Sdougb */ 612224090Sdougb name = NULL; 613224090Sdougb dns_message_currentname(rmessage, 614224090Sdougb DNS_SECTION_AUTHORITY, 615224090Sdougb &name); 616224090Sdougb for (rdataset = ISC_LIST_HEAD(name->list); 617224090Sdougb rdataset != NULL; 618224090Sdougb rdataset = ISC_LIST_NEXT(rdataset, 619224090Sdougb link)) { 620224090Sdougb if (rdataset->type == 621224090Sdougb dns_rdatatype_soa && 622224090Sdougb dns_rdataset_count(rdataset) > 1) { 623224090Sdougb *resultp = multiplesoa; 624224090Sdougb goto found; 625224090Sdougb } 626224090Sdougb } 627224090Sdougb result = dns_message_nextname(rmessage, 628224090Sdougb DNS_SECTION_AUTHORITY); 629224090Sdougb } 630224090Sdougb } 631224090Sdougb } else if (rev->result == ISC_R_TIMEDOUT) 632224090Sdougb *resultp = timedout; 633224090Sdougb else { 634224090Sdougb fprintf(stderr, "unexpected result: %d (domain=%s, server=", 635224090Sdougb rev->result, trans->domain); 636224090Sdougb print_address(stderr, &server->address); 637224090Sdougb fputc('\n', stderr); 638224090Sdougb *resultp = unexpected; 639224090Sdougb } 640224090Sdougb 641224090Sdougb found: 642224090Sdougb INSIST(*resultp != none); 643224090Sdougb if (type == dns_rdatatype_a && *resultp == exist) 644224090Sdougb trans->qname_found = ISC_TRUE; 645224090Sdougb 646224090Sdougb dns_client_destroyreqtrans(&trans->reqid); 647224090Sdougb isc_event_free(&event); 648224090Sdougb dns_message_reset(trans->rmessage, DNS_MESSAGE_INTENTPARSE); 649224090Sdougb 650224090Sdougb result = probe_name(trans, type); 651224090Sdougb if (result == ISC_R_NOMORE) { 652224090Sdougb /* We've tried all addresses of all servers. */ 653224090Sdougb if (type == dns_rdatatype_a && trans->qname_found) { 654224090Sdougb /* 655224090Sdougb * If we've explored A RRs and found an existent 656224090Sdougb * record, we can move to AAAA. 657224090Sdougb */ 658224090Sdougb trans->current_ns = ISC_LIST_HEAD(trans->nslist); 659224090Sdougb probe_name(trans, dns_rdatatype_aaaa); 660224090Sdougb result = ISC_R_SUCCESS; 661224090Sdougb } else if (type == dns_rdatatype_a) { 662224090Sdougb /* 663224090Sdougb * No server provided an existent A RR of this name. 664224090Sdougb * Try next label. 665224090Sdougb */ 666224090Sdougb dns_fixedname_invalidate(&trans->fixedname); 667224090Sdougb trans->qname = NULL; 668224090Sdougb result = set_nextqname(trans); 669224090Sdougb if (result == ISC_R_SUCCESS) { 670224090Sdougb trans->current_ns = 671224090Sdougb ISC_LIST_HEAD(trans->nslist); 672224090Sdougb for (pns = trans->current_ns; pns != NULL; 673224090Sdougb pns = ISC_LIST_NEXT(pns, link)) { 674224090Sdougb for (server = ISC_LIST_HEAD(pns->servers); 675224090Sdougb server != NULL; 676224090Sdougb server = ISC_LIST_NEXT(server, 677224090Sdougb link)) { 678224090Sdougb INSIST(server->result_aaaa == 679224090Sdougb none); 680224090Sdougb server->result_a = none; 681224090Sdougb } 682224090Sdougb } 683224090Sdougb result = probe_name(trans, dns_rdatatype_a); 684224090Sdougb } 685224090Sdougb } 686224090Sdougb if (result != ISC_R_SUCCESS) { 687224090Sdougb /* 688224090Sdougb * We've explored AAAA RRs or failed to find a valid 689224090Sdougb * query label. Wrap up the result and move to the 690224090Sdougb * next domain. 691224090Sdougb */ 692224090Sdougb reset_probe(trans); 693224090Sdougb } 694224090Sdougb } else if (result != ISC_R_SUCCESS) 695224090Sdougb reset_probe(trans); /* XXX */ 696224090Sdougb} 697224090Sdougb 698224090Sdougbstatic isc_result_t 699224090Sdougbprobe_name(struct probe_trans *trans, dns_rdatatype_t type) { 700224090Sdougb isc_result_t result; 701224090Sdougb struct probe_ns *pns; 702224090Sdougb struct server *server; 703224090Sdougb 704224090Sdougb REQUIRE(trans->reqid == NULL); 705224090Sdougb REQUIRE(type == dns_rdatatype_a || type == dns_rdatatype_aaaa); 706224090Sdougb 707224090Sdougb for (pns = trans->current_ns; pns != NULL; 708224090Sdougb pns = ISC_LIST_NEXT(pns, link)) { 709224090Sdougb for (server = ISC_LIST_HEAD(pns->servers); server != NULL; 710224090Sdougb server = ISC_LIST_NEXT(server, link)) { 711224090Sdougb if ((type == dns_rdatatype_a && 712224090Sdougb server->result_a == none) || 713224090Sdougb (type == dns_rdatatype_aaaa && 714224090Sdougb server->result_aaaa == none)) { 715224090Sdougb pns->current_server = server; 716224090Sdougb goto found; 717224090Sdougb } 718224090Sdougb } 719224090Sdougb } 720224090Sdougb 721224090Sdougb found: 722224090Sdougb trans->current_ns = pns; 723224090Sdougb if (pns == NULL) 724224090Sdougb return (ISC_R_NOMORE); 725224090Sdougb 726224090Sdougb INSIST(pns->current_server != NULL); 727224090Sdougb dns_message_reset(trans->qmessage, DNS_MESSAGE_INTENTRENDER); 728224090Sdougb result = make_querymessage(trans->qmessage, trans->qname, type); 729224090Sdougb if (result != ISC_R_SUCCESS) 730224090Sdougb return (result); 731224090Sdougb result = dns_client_startrequest(client, trans->qmessage, 732224090Sdougb trans->rmessage, 733224090Sdougb &pns->current_server->address, 734224090Sdougb 0, DNS_MESSAGEPARSE_BESTEFFORT, 735224090Sdougb NULL, 120, 0, 4, 736224090Sdougb probe_task, request_done, trans, 737224090Sdougb &trans->reqid); 738224090Sdougb 739224090Sdougb return (result); 740224090Sdougb} 741224090Sdougb 742224090Sdougb/* 743224090Sdougb * Get IP addresses of NSes 744224090Sdougb */ 745224090Sdougb 746224090Sdougbstatic void 747224090Sdougbresolve_nsaddress(isc_task_t *task, isc_event_t *event) { 748224090Sdougb struct probe_trans *trans = event->ev_arg; 749224090Sdougb dns_clientresevent_t *rev = (dns_clientresevent_t *)event; 750224090Sdougb dns_name_t *name; 751224090Sdougb dns_rdataset_t *rdataset; 752224090Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 753224090Sdougb struct probe_ns *pns = trans->current_ns; 754224090Sdougb isc_result_t result; 755224090Sdougb 756224090Sdougb REQUIRE(task == probe_task); 757224090Sdougb REQUIRE(trans->inuse == ISC_TRUE); 758224090Sdougb REQUIRE(pns != NULL); 759224090Sdougb INSIST(outstanding_probes > 0); 760224090Sdougb 761224090Sdougb for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL; 762224090Sdougb name = ISC_LIST_NEXT(name, link)) { 763224090Sdougb for (rdataset = ISC_LIST_HEAD(name->list); 764224090Sdougb rdataset != NULL; 765224090Sdougb rdataset = ISC_LIST_NEXT(rdataset, link)) { 766224090Sdougb (void)print_rdataset(rdataset, name); 767224090Sdougb 768224090Sdougb if (rdataset->type != dns_rdatatype_a) 769224090Sdougb continue; 770224090Sdougb 771224090Sdougb for (result = dns_rdataset_first(rdataset); 772224090Sdougb result == ISC_R_SUCCESS; 773224090Sdougb result = dns_rdataset_next(rdataset)) { 774224090Sdougb dns_rdata_in_a_t rdata_a; 775224090Sdougb struct server *server; 776224090Sdougb 777224090Sdougb dns_rdataset_current(rdataset, &rdata); 778224090Sdougb result = dns_rdata_tostruct(&rdata, &rdata_a, 779224090Sdougb NULL); 780224090Sdougb if (result != ISC_R_SUCCESS) 781224090Sdougb continue; 782224090Sdougb 783224090Sdougb server = isc_mem_get(mctx, sizeof(*server)); 784224090Sdougb if (server == NULL) { 785224090Sdougb fprintf(stderr, "resolve_nsaddress: " 786224090Sdougb "mem_get failed"); 787224090Sdougb result = ISC_R_NOMEMORY; 788225361Sdougb POST(result); 789224090Sdougb goto cleanup; 790224090Sdougb } 791224090Sdougb isc_sockaddr_fromin(&server->address, 792224090Sdougb &rdata_a.in_addr, 53); 793224090Sdougb ISC_LINK_INIT(server, link); 794224090Sdougb server->result_a = none; 795224090Sdougb server->result_aaaa = none; 796224090Sdougb ISC_LIST_APPEND(pns->servers, server, link); 797224090Sdougb } 798224090Sdougb } 799224090Sdougb } 800224090Sdougb 801224090Sdougb cleanup: 802224090Sdougb dns_client_freeresanswer(client, &rev->answerlist); 803224090Sdougb dns_client_destroyrestrans(&trans->resid); 804224090Sdougb isc_event_free(&event); 805224090Sdougb 806224090Sdougb next_ns: 807224090Sdougb trans->current_ns = ISC_LIST_NEXT(pns, link); 808224090Sdougb if (trans->current_ns == NULL) { 809224090Sdougb trans->current_ns = ISC_LIST_HEAD(trans->nslist); 810224090Sdougb dns_fixedname_invalidate(&trans->fixedname); 811224090Sdougb trans->qname = NULL; 812224090Sdougb result = set_nextqname(trans); 813224090Sdougb if (result == ISC_R_SUCCESS) 814224090Sdougb result = probe_name(trans, dns_rdatatype_a); 815224090Sdougb } else { 816224090Sdougb result = fetch_nsaddress(trans); 817224090Sdougb if (result != ISC_R_SUCCESS) 818224090Sdougb goto next_ns; /* XXX: this is unlikely to succeed */ 819224090Sdougb } 820224090Sdougb 821224090Sdougb if (result != ISC_R_SUCCESS) 822224090Sdougb reset_probe(trans); 823224090Sdougb} 824224090Sdougb 825224090Sdougbstatic isc_result_t 826224090Sdougbfetch_nsaddress(struct probe_trans *trans) { 827224090Sdougb struct probe_ns *pns; 828224090Sdougb 829224090Sdougb pns = trans->current_ns; 830224090Sdougb REQUIRE(pns != NULL); 831224090Sdougb 832224090Sdougb return (dns_client_startresolve(client, pns->name, dns_rdataclass_in, 833224090Sdougb dns_rdatatype_a, 0, probe_task, 834224090Sdougb resolve_nsaddress, trans, 835224090Sdougb &trans->resid)); 836224090Sdougb} 837224090Sdougb 838224090Sdougb/* 839224090Sdougb * Get NS RRset for a given domain 840224090Sdougb */ 841224090Sdougb 842224090Sdougbstatic void 843224090Sdougbreset_probe(struct probe_trans *trans) { 844224090Sdougb struct probe_ns *pns; 845224090Sdougb struct server *server; 846224090Sdougb isc_result_t result; 847224090Sdougb 848224090Sdougb REQUIRE(trans->resid == NULL); 849224090Sdougb REQUIRE(trans->reqid == NULL); 850224090Sdougb 851224090Sdougb update_stat(trans); 852224090Sdougb 853224090Sdougb dns_message_reset(trans->qmessage, DNS_MESSAGE_INTENTRENDER); 854224090Sdougb dns_message_reset(trans->rmessage, DNS_MESSAGE_INTENTPARSE); 855224090Sdougb 856224090Sdougb trans->inuse = ISC_FALSE; 857224090Sdougb if (trans->domain != NULL) 858224090Sdougb isc_mem_free(mctx, trans->domain); 859224090Sdougb trans->domain = NULL; 860224090Sdougb if (trans->qname != NULL) 861224090Sdougb dns_fixedname_invalidate(&trans->fixedname); 862224090Sdougb trans->qname = NULL; 863224090Sdougb trans->qlabel = qlabels; 864224090Sdougb trans->qname_found = ISC_FALSE; 865224090Sdougb trans->current_ns = NULL; 866224090Sdougb 867224090Sdougb while ((pns = ISC_LIST_HEAD(trans->nslist)) != NULL) { 868224090Sdougb ISC_LIST_UNLINK(trans->nslist, pns, link); 869224090Sdougb while ((server = ISC_LIST_HEAD(pns->servers)) != NULL) { 870224090Sdougb ISC_LIST_UNLINK(pns->servers, server, link); 871224090Sdougb isc_mem_put(mctx, server, sizeof(*server)); 872224090Sdougb } 873224090Sdougb isc_mem_put(mctx, pns, sizeof(*pns)); 874224090Sdougb } 875224090Sdougb 876224090Sdougb outstanding_probes--; 877224090Sdougb 878224090Sdougb result = probe_domain(trans); 879224090Sdougb if (result == ISC_R_NOMORE && outstanding_probes == 0) 880224090Sdougb isc_app_ctxshutdown(actx); 881224090Sdougb} 882224090Sdougb 883224090Sdougbstatic void 884224090Sdougbresolve_ns(isc_task_t *task, isc_event_t *event) { 885224090Sdougb struct probe_trans *trans = event->ev_arg; 886224090Sdougb dns_clientresevent_t *rev = (dns_clientresevent_t *)event; 887224090Sdougb dns_name_t *name; 888224090Sdougb dns_rdataset_t *rdataset; 889224090Sdougb isc_result_t result = ISC_R_SUCCESS; 890224090Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 891224090Sdougb struct probe_ns *pns; 892224090Sdougb 893224090Sdougb REQUIRE(task == probe_task); 894224090Sdougb REQUIRE(trans->inuse == ISC_TRUE); 895224090Sdougb INSIST(outstanding_probes > 0); 896224090Sdougb 897224090Sdougb for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL; 898224090Sdougb name = ISC_LIST_NEXT(name, link)) { 899224090Sdougb for (rdataset = ISC_LIST_HEAD(name->list); 900224090Sdougb rdataset != NULL; 901224090Sdougb rdataset = ISC_LIST_NEXT(rdataset, link)) { 902224090Sdougb (void)print_rdataset(rdataset, name); 903224090Sdougb 904224090Sdougb if (rdataset->type != dns_rdatatype_ns) 905224090Sdougb continue; 906224090Sdougb 907224090Sdougb for (result = dns_rdataset_first(rdataset); 908224090Sdougb result == ISC_R_SUCCESS; 909224090Sdougb result = dns_rdataset_next(rdataset)) { 910224090Sdougb dns_rdata_ns_t ns; 911224090Sdougb 912224090Sdougb dns_rdataset_current(rdataset, &rdata); 913224090Sdougb /* 914224090Sdougb * Extract the name from the NS record. 915224090Sdougb */ 916224090Sdougb result = dns_rdata_tostruct(&rdata, &ns, NULL); 917224090Sdougb if (result != ISC_R_SUCCESS) 918224090Sdougb continue; 919224090Sdougb 920224090Sdougb pns = isc_mem_get(mctx, sizeof(*pns)); 921224090Sdougb if (pns == NULL) { 922224090Sdougb fprintf(stderr, 923224090Sdougb "resolve_ns: mem_get failed"); 924224090Sdougb result = ISC_R_NOMEMORY; 925225361Sdougb POST(result); 926224090Sdougb /* 927224090Sdougb * XXX: should we continue with the 928224090Sdougb * available servers anyway? 929224090Sdougb */ 930224090Sdougb goto cleanup; 931224090Sdougb } 932224090Sdougb 933224090Sdougb dns_fixedname_init(&pns->fixedname); 934224090Sdougb pns->name = 935224090Sdougb dns_fixedname_name(&pns->fixedname); 936224090Sdougb ISC_LINK_INIT(pns, link); 937224090Sdougb ISC_LIST_APPEND(trans->nslist, pns, link); 938224090Sdougb ISC_LIST_INIT(pns->servers); 939224090Sdougb 940224090Sdougb dns_name_copy(&ns.name, pns->name, NULL); 941224090Sdougb dns_rdata_reset(&rdata); 942224090Sdougb dns_rdata_freestruct(&ns); 943224090Sdougb } 944224090Sdougb } 945224090Sdougb } 946224090Sdougb 947224090Sdougb cleanup: 948224090Sdougb dns_client_freeresanswer(client, &rev->answerlist); 949224090Sdougb dns_client_destroyrestrans(&trans->resid); 950224090Sdougb isc_event_free(&event); 951224090Sdougb 952224090Sdougb if (!ISC_LIST_EMPTY(trans->nslist)) { 953224090Sdougb /* Go get addresses of NSes */ 954224090Sdougb trans->current_ns = ISC_LIST_HEAD(trans->nslist); 955224090Sdougb result = fetch_nsaddress(trans); 956224090Sdougb } else 957224090Sdougb result = ISC_R_FAILURE; 958224090Sdougb 959224090Sdougb if (result == ISC_R_SUCCESS) 960224090Sdougb return; 961224090Sdougb 962224090Sdougb reset_probe(trans); 963224090Sdougb} 964224090Sdougb 965224090Sdougbstatic isc_result_t 966224090Sdougbprobe_domain(struct probe_trans *trans) { 967224090Sdougb isc_result_t result; 968224090Sdougb size_t domainlen; 969224090Sdougb isc_buffer_t b; 970224090Sdougb char buf[4096]; /* XXX ad hoc constant, but should be enough */ 971224090Sdougb char *cp; 972224090Sdougb 973224090Sdougb REQUIRE(trans != NULL); 974224090Sdougb REQUIRE(trans->inuse == ISC_FALSE); 975224090Sdougb REQUIRE(outstanding_probes < MAX_PROBES); 976224090Sdougb 977224090Sdougb /* Construct domain */ 978224090Sdougb cp = fgets(buf, sizeof(buf), fp); 979224090Sdougb if (cp == NULL) 980224090Sdougb return (ISC_R_NOMORE); 981224090Sdougb if ((cp = strchr(buf, '\n')) != NULL) /* zap NL if any */ 982224090Sdougb *cp = '\0'; 983224090Sdougb trans->domain = isc_mem_strdup(mctx, buf); 984224090Sdougb if (trans->domain == NULL) { 985224090Sdougb fprintf(stderr, 986224090Sdougb "failed to allocate memory for domain: %s", cp); 987224090Sdougb return (ISC_R_NOMEMORY); 988224090Sdougb } 989224090Sdougb 990224090Sdougb /* Start getting NS for the domain */ 991224090Sdougb domainlen = strlen(buf); 992224090Sdougb isc_buffer_init(&b, buf, domainlen); 993224090Sdougb isc_buffer_add(&b, domainlen); 994224090Sdougb dns_fixedname_init(&trans->fixedname); 995224090Sdougb trans->qname = dns_fixedname_name(&trans->fixedname); 996224090Sdougb result = dns_name_fromtext(trans->qname, &b, dns_rootname, 0, NULL); 997224090Sdougb if (result != ISC_R_SUCCESS) 998224090Sdougb goto cleanup; 999224090Sdougb result = dns_client_startresolve(client, trans->qname, 1000224090Sdougb dns_rdataclass_in, dns_rdatatype_ns, 1001224090Sdougb 0, probe_task, resolve_ns, trans, 1002224090Sdougb &trans->resid); 1003224090Sdougb if (result != ISC_R_SUCCESS) 1004224090Sdougb goto cleanup; 1005224090Sdougb 1006224090Sdougb trans->inuse = ISC_TRUE; 1007224090Sdougb outstanding_probes++; 1008224090Sdougb 1009224090Sdougb return (ISC_R_SUCCESS); 1010224090Sdougb 1011224090Sdougb cleanup: 1012224090Sdougb isc_mem_free(mctx, trans->domain); 1013224090Sdougb dns_fixedname_invalidate(&trans->fixedname); 1014224090Sdougb 1015224090Sdougb return (result); 1016224090Sdougb} 1017224090Sdougb 1018224090SdougbISC_PLATFORM_NORETURN_PRE static void 1019224090Sdougbusage(void) ISC_PLATFORM_NORETURN_POST; 1020224090Sdougb 1021224090Sdougbstatic void 1022224090Sdougbusage(void) { 1023224090Sdougb fprintf(stderr, "usage: nsprobe [-d] [-v [-v...]] [-c cache_address] " 1024224090Sdougb "[input_file]\n"); 1025224090Sdougb 1026224090Sdougb exit(1); 1027224090Sdougb} 1028224090Sdougb 1029224090Sdougbint 1030224090Sdougbmain(int argc, char *argv[]) { 1031224090Sdougb int i, ch, error; 1032224090Sdougb struct addrinfo hints, *res; 1033224090Sdougb isc_result_t result; 1034224090Sdougb isc_sockaddr_t sa; 1035224090Sdougb isc_sockaddrlist_t servers; 1036224090Sdougb isc_taskmgr_t *taskmgr = NULL; 1037224090Sdougb isc_socketmgr_t *socketmgr = NULL; 1038224090Sdougb isc_timermgr_t *timermgr = NULL; 1039224090Sdougb 1040224090Sdougb while ((ch = getopt(argc, argv, "c:dhv")) != -1) { 1041224090Sdougb switch (ch) { 1042224090Sdougb case 'c': 1043224090Sdougb cacheserver = optarg; 1044224090Sdougb break; 1045224090Sdougb case 'd': 1046224090Sdougb debug_mode = ISC_TRUE; 1047224090Sdougb break; 1048224090Sdougb case 'h': 1049224090Sdougb usage(); 1050224090Sdougb break; 1051224090Sdougb case 'v': 1052224090Sdougb verbose_level++; 1053224090Sdougb break; 1054224090Sdougb default: 1055224090Sdougb usage(); 1056224090Sdougb break; 1057224090Sdougb } 1058224090Sdougb } 1059224090Sdougb 1060224090Sdougb argc -= optind; 1061224090Sdougb argv += optind; 1062224090Sdougb 1063224090Sdougb /* Common set up */ 1064224090Sdougb isc_lib_register(); 1065224090Sdougb result = dns_lib_init(); 1066224090Sdougb if (result != ISC_R_SUCCESS) { 1067224090Sdougb fprintf(stderr, "dns_lib_init failed: %d\n", result); 1068224090Sdougb exit(1); 1069224090Sdougb } 1070224090Sdougb 1071224090Sdougb result = ctxs_init(&mctx, &actx, &taskmgr, &socketmgr, 1072224090Sdougb &timermgr); 1073224090Sdougb if (result != ISC_R_SUCCESS) { 1074224090Sdougb fprintf(stderr, "ctx create failed: %d\n", result); 1075224090Sdougb exit(1); 1076224090Sdougb } 1077224090Sdougb 1078224090Sdougb isc_app_ctxstart(actx); 1079224090Sdougb 1080224090Sdougb result = dns_client_createx(mctx, actx, taskmgr, socketmgr, 1081224090Sdougb timermgr, 0, &client); 1082224090Sdougb if (result != ISC_R_SUCCESS) { 1083224090Sdougb fprintf(stderr, "dns_client_createx failed: %d\n", result); 1084224090Sdougb exit(1); 1085224090Sdougb } 1086224090Sdougb 1087224090Sdougb /* Set local cache server */ 1088224090Sdougb memset(&hints, 0, sizeof(hints)); 1089224090Sdougb hints.ai_family = AF_UNSPEC; 1090224090Sdougb hints.ai_socktype = SOCK_DGRAM; 1091224090Sdougb error = getaddrinfo(cacheserver, "53", &hints, &res); 1092224090Sdougb if (error != 0) { 1093224090Sdougb fprintf(stderr, "failed to convert server name (%s): %s\n", 1094224090Sdougb cacheserver, gai_strerror(error)); 1095224090Sdougb exit(1); 1096224090Sdougb } 1097224090Sdougb 1098224090Sdougb if (res->ai_addrlen > sizeof(sa.type)) { 1099224090Sdougb fprintf(stderr, 1100225361Sdougb "assumption failure: addrlen is too long: %ld\n", 1101225361Sdougb (long)res->ai_addrlen); 1102224090Sdougb exit(1); 1103224090Sdougb } 1104262706Serwin memmove(&sa.type.sa, res->ai_addr, res->ai_addrlen); 1105224090Sdougb sa.length = res->ai_addrlen; 1106224090Sdougb freeaddrinfo(res); 1107224090Sdougb ISC_LINK_INIT(&sa, link); 1108224090Sdougb ISC_LIST_INIT(servers); 1109224090Sdougb ISC_LIST_APPEND(servers, &sa, link); 1110224090Sdougb result = dns_client_setservers(client, dns_rdataclass_in, NULL, 1111224090Sdougb &servers); 1112224090Sdougb if (result != ISC_R_SUCCESS) { 1113224090Sdougb fprintf(stderr, "failed to set server: %d\n", result); 1114224090Sdougb exit(1); 1115224090Sdougb } 1116224090Sdougb 1117224090Sdougb /* Create the main task */ 1118224090Sdougb probe_task = NULL; 1119224090Sdougb result = isc_task_create(taskmgr, 0, &probe_task); 1120224090Sdougb if (result != ISC_R_SUCCESS) { 1121224090Sdougb fprintf(stderr, "failed to create task: %d\n", result); 1122224090Sdougb exit(1); 1123224090Sdougb } 1124224090Sdougb 1125224090Sdougb /* Open input file */ 1126224090Sdougb if (argc == 0) 1127224090Sdougb fp = stdin; 1128224090Sdougb else { 1129224090Sdougb fp = fopen(argv[0], "r"); 1130224090Sdougb if (fp == NULL) { 1131224090Sdougb fprintf(stderr, "failed to open input file: %s\n", 1132224090Sdougb argv[0]); 1133224090Sdougb exit(1); 1134224090Sdougb } 1135224090Sdougb } 1136224090Sdougb 1137224090Sdougb /* Set up and start probe */ 1138224090Sdougb for (i = 0; i < MAX_PROBES; i++) { 1139224090Sdougb probes[i].inuse = ISC_FALSE; 1140224090Sdougb probes[i].domain = NULL; 1141224090Sdougb dns_fixedname_init(&probes[i].fixedname); 1142224090Sdougb probes[i].qname = NULL; 1143224090Sdougb probes[i].qlabel = qlabels; 1144224090Sdougb probes[i].qname_found = ISC_FALSE; 1145224090Sdougb probes[i].resid = NULL; 1146224090Sdougb ISC_LIST_INIT(probes[i].nslist); 1147224090Sdougb probes[i].reqid = NULL; 1148224090Sdougb 1149224090Sdougb probes[i].qmessage = NULL; 1150224090Sdougb result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, 1151224090Sdougb &probes[i].qmessage); 1152224090Sdougb if (result == ISC_R_SUCCESS) { 1153224090Sdougb result = dns_message_create(mctx, 1154224090Sdougb DNS_MESSAGE_INTENTPARSE, 1155224090Sdougb &probes[i].rmessage); 1156224090Sdougb } 1157224090Sdougb if (result != ISC_R_SUCCESS) { 1158224090Sdougb fprintf(stderr, "initialization failure\n"); 1159224090Sdougb exit(1); 1160224090Sdougb } 1161224090Sdougb } 1162224090Sdougb for (i = 0; i < MAX_PROBES; i++) { 1163224090Sdougb result = probe_domain(&probes[i]); 1164224090Sdougb if (result == ISC_R_NOMORE) 1165224090Sdougb break; 1166224090Sdougb else if (result != ISC_R_SUCCESS) { 1167224090Sdougb fprintf(stderr, "failed to issue an initial probe\n"); 1168224090Sdougb exit(1); 1169224090Sdougb } 1170224090Sdougb } 1171224090Sdougb 1172224090Sdougb /* Start event loop */ 1173224090Sdougb isc_app_ctxrun(actx); 1174224090Sdougb 1175224090Sdougb /* Dump results */ 1176224090Sdougb printf("Per domain results (out of %lu domains):\n", 1177224090Sdougb number_of_domains); 1178224090Sdougb printf(" valid: %lu\n" 1179224090Sdougb " ignore: %lu\n" 1180224090Sdougb " nxdomain: %lu\n" 1181224090Sdougb " othererr: %lu\n" 1182224090Sdougb " multiplesoa: %lu\n" 1183224090Sdougb " multiplecname: %lu\n" 1184224090Sdougb " brokenanswer: %lu\n" 1185224090Sdougb " lame: %lu\n" 1186224090Sdougb " unknown: %lu\n" 1187224090Sdougb " multiple errors: %lu\n", 1188224090Sdougb domain_stat.valid, domain_stat.ignore, domain_stat.nxdomain, 1189224090Sdougb domain_stat.othererr, domain_stat.multiplesoa, 1190224090Sdougb domain_stat.multiplecname, domain_stat.brokenanswer, 1191224090Sdougb domain_stat.lame, domain_stat.unknown, multiple_error_domains); 1192224090Sdougb printf("Per server results (out of %lu servers):\n", 1193224090Sdougb number_of_servers); 1194224090Sdougb printf(" valid: %lu\n" 1195224090Sdougb " ignore: %lu\n" 1196224090Sdougb " nxdomain: %lu\n" 1197224090Sdougb " othererr: %lu\n" 1198224090Sdougb " multiplesoa: %lu\n" 1199224090Sdougb " multiplecname: %lu\n" 1200224090Sdougb " brokenanswer: %lu\n" 1201224090Sdougb " lame: %lu\n" 1202224090Sdougb " unknown: %lu\n", 1203224090Sdougb server_stat.valid, server_stat.ignore, server_stat.nxdomain, 1204224090Sdougb server_stat.othererr, server_stat.multiplesoa, 1205224090Sdougb server_stat.multiplecname, server_stat.brokenanswer, 1206224090Sdougb server_stat.lame, server_stat.unknown); 1207224090Sdougb 1208224090Sdougb /* Cleanup */ 1209224090Sdougb for (i = 0; i < MAX_PROBES; i++) { 1210224090Sdougb dns_message_destroy(&probes[i].qmessage); 1211224090Sdougb dns_message_destroy(&probes[i].rmessage); 1212224090Sdougb } 1213224090Sdougb isc_task_detach(&probe_task); 1214224090Sdougb dns_client_destroy(&client); 1215224090Sdougb dns_lib_shutdown(); 1216224090Sdougb isc_app_ctxfinish(actx); 1217224090Sdougb ctxs_destroy(&mctx, &actx, &taskmgr, &socketmgr, &timermgr); 1218224090Sdougb 1219254402Serwin return (0); 1220224090Sdougb} 1221