/* * Copyright (C) 2009-2011 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: nsprobe.c,v 1.7.180.3 2011-04-05 06:35:00 marka Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_PROBES 1000 static dns_client_t *client = NULL; static isc_task_t *probe_task = NULL; static isc_appctx_t *actx = NULL; static isc_mem_t *mctx = NULL; static unsigned int outstanding_probes = 0; const char *cacheserver = "127.0.0.1"; static FILE *fp; typedef enum { none, exist, nxdomain, othererr, multiplesoa, multiplecname, brokenanswer, lame, timedout, notype, unexpected } query_result_t; struct server { ISC_LINK(struct server) link; isc_sockaddr_t address; query_result_t result_a; query_result_t result_aaaa; }; struct probe_ns { ISC_LINK(struct probe_ns) link; dns_fixedname_t fixedname; dns_name_t *name; struct server *current_server; ISC_LIST(struct server) servers; }; struct probe_trans { isc_boolean_t inuse; char *domain; dns_fixedname_t fixedname; dns_name_t *qname; const char **qlabel; isc_boolean_t qname_found; dns_clientrestrans_t *resid; dns_message_t *qmessage; dns_message_t *rmessage; dns_clientreqtrans_t *reqid; /* NS list */ struct probe_ns *current_ns; ISC_LIST(struct probe_ns) nslist; }; struct lcl_stat { unsigned long valid; unsigned long ignore; unsigned long nxdomain; unsigned long othererr; unsigned long multiplesoa; unsigned long multiplecname; unsigned long brokenanswer; unsigned long lame; unsigned long unknown; } server_stat, domain_stat; static unsigned long number_of_domains = 0; static unsigned long number_of_servers = 0; static unsigned long multiple_error_domains = 0; static isc_boolean_t debug_mode = ISC_FALSE; static int verbose_level = 0; static const char *qlabels[] = {"www.", "ftp.", NULL}; static struct probe_trans probes[MAX_PROBES]; static isc_result_t probe_domain(struct probe_trans *trans); static void reset_probe(struct probe_trans *trans); static isc_result_t fetch_nsaddress(struct probe_trans *trans); static isc_result_t probe_name(struct probe_trans *trans, dns_rdatatype_t type); /* Dump an rdataset for debug */ static isc_result_t print_rdataset(dns_rdataset_t *rdataset, dns_name_t *owner) { isc_buffer_t target; isc_result_t result; isc_region_t r; char t[4096]; if (!debug_mode) return (ISC_R_SUCCESS); isc_buffer_init(&target, t, sizeof(t)); if (!dns_rdataset_isassociated(rdataset)) return (ISC_R_SUCCESS); result = dns_rdataset_totext(rdataset, owner, ISC_FALSE, ISC_FALSE, &target); if (result != ISC_R_SUCCESS) return (result); isc_buffer_usedregion(&target, &r); printf("%.*s", (int)r.length, (char *)r.base); return (ISC_R_SUCCESS); } static isc_result_t print_name(dns_name_t *name) { isc_result_t result; isc_buffer_t target; isc_region_t r; char t[4096]; isc_buffer_init(&target, t, sizeof(t)); result = dns_name_totext(name, ISC_TRUE, &target); if (result == ISC_R_SUCCESS) { isc_buffer_usedregion(&target, &r); printf("%.*s", (int)r.length, (char *)r.base); } else printf("(invalid name)"); return (result); } static isc_result_t print_address(FILE *fp, isc_sockaddr_t *addr) { char buf[NI_MAXHOST]; if (getnameinfo(&addr->type.sa, addr->length, buf, sizeof(buf), NULL, 0, NI_NUMERICHOST) == 0) { fprintf(fp, "%s", buf); } else { fprintf(fp, "(invalid address)"); } return (ISC_R_SUCCESS); } static void ctxs_destroy(isc_mem_t **mctxp, isc_appctx_t **actxp, isc_taskmgr_t **taskmgrp, isc_socketmgr_t **socketmgrp, isc_timermgr_t **timermgrp) { if (*taskmgrp != NULL) isc_taskmgr_destroy(taskmgrp); if (*timermgrp != NULL) isc_timermgr_destroy(timermgrp); if (*socketmgrp != NULL) isc_socketmgr_destroy(socketmgrp); if (*actxp != NULL) isc_appctx_destroy(actxp); if (*mctxp != NULL) isc_mem_destroy(mctxp); } static isc_result_t ctxs_init(isc_mem_t **mctxp, isc_appctx_t **actxp, isc_taskmgr_t **taskmgrp, isc_socketmgr_t **socketmgrp, isc_timermgr_t **timermgrp) { isc_result_t result; result = isc_mem_create(0, 0, mctxp); if (result != ISC_R_SUCCESS) goto fail; result = isc_appctx_create(*mctxp, actxp); if (result != ISC_R_SUCCESS) goto fail; result = isc_taskmgr_createinctx(*mctxp, *actxp, 1, 0, taskmgrp); if (result != ISC_R_SUCCESS) goto fail; result = isc_socketmgr_createinctx(*mctxp, *actxp, socketmgrp); if (result != ISC_R_SUCCESS) goto fail; result = isc_timermgr_createinctx(*mctxp, *actxp, timermgrp); if (result != ISC_R_SUCCESS) goto fail; return (ISC_R_SUCCESS); fail: ctxs_destroy(mctxp, actxp, taskmgrp, socketmgrp, timermgrp); return (result); } /* * Common routine to make query data */ static isc_result_t make_querymessage(dns_message_t *message, dns_name_t *qname0, dns_rdatatype_t rdtype) { dns_name_t *qname = NULL; dns_rdataset_t *qrdataset = NULL; isc_result_t result; message->opcode = dns_opcode_query; message->rdclass = dns_rdataclass_in; result = dns_message_gettempname(message, &qname); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_message_gettemprdataset(message, &qrdataset); if (result != ISC_R_SUCCESS) goto cleanup; dns_name_init(qname, NULL); dns_name_clone(qname0, qname); dns_rdataset_init(qrdataset); dns_rdataset_makequestion(qrdataset, message->rdclass, rdtype); ISC_LIST_APPEND(qname->list, qrdataset, link); dns_message_addname(message, qname, DNS_SECTION_QUESTION); return (ISC_R_SUCCESS); cleanup: if (qname != NULL) dns_message_puttempname(message, &qname); if (qrdataset != NULL) dns_message_puttemprdataset(message, &qrdataset); if (message != NULL) dns_message_destroy(&message); return (result); } /* * Update statistics */ static inline void increment_entry(unsigned long *entryp) { (*entryp)++; INSIST(*entryp != 0); /* check overflow */ } static void update_stat(struct probe_trans *trans) { struct probe_ns *pns; struct server *server; struct lcl_stat local_stat; unsigned int err_count = 0; const char *stattype; increment_entry(&number_of_domains); memset(&local_stat, 0, sizeof(local_stat)); /* Update per sever statistics */ for (pns = ISC_LIST_HEAD(trans->nslist); pns != NULL; pns = ISC_LIST_NEXT(pns, link)) { for (server = ISC_LIST_HEAD(pns->servers); server != NULL; server = ISC_LIST_NEXT(server, link)) { increment_entry(&number_of_servers); if (server->result_aaaa == exist || server->result_aaaa == notype) { /* * Don't care about the result of A query if * the answer to AAAA query was expected. */ stattype = "valid"; increment_entry(&server_stat.valid); increment_entry(&local_stat.valid); } else if (server->result_a == exist) { switch (server->result_aaaa) { case exist: case notype: stattype = "valid"; increment_entry(&server_stat.valid); increment_entry(&local_stat.valid); break; case timedout: stattype = "ignore"; increment_entry(&server_stat.ignore); increment_entry(&local_stat.ignore); break; case nxdomain: stattype = "nxdomain"; increment_entry(&server_stat.nxdomain); increment_entry(&local_stat.nxdomain); break; case othererr: stattype = "othererr"; increment_entry(&server_stat.othererr); increment_entry(&local_stat.othererr); break; case multiplesoa: stattype = "multiplesoa"; increment_entry(&server_stat.multiplesoa); increment_entry(&local_stat.multiplesoa); break; case multiplecname: stattype = "multiplecname"; increment_entry(&server_stat.multiplecname); increment_entry(&local_stat.multiplecname); break; case brokenanswer: stattype = "brokenanswer"; increment_entry(&server_stat.brokenanswer); increment_entry(&local_stat.brokenanswer); break; case lame: stattype = "lame"; increment_entry(&server_stat.lame); increment_entry(&local_stat.lame); break; default: stattype = "unknown"; increment_entry(&server_stat.unknown); increment_entry(&local_stat.unknown); break; } } else { stattype = "unknown"; increment_entry(&server_stat.unknown); increment_entry(&local_stat.unknown); } if (verbose_level > 1 || (verbose_level == 1 && strcmp(stattype, "valid") != 0 && strcmp(stattype, "unknown") != 0)) { print_name(pns->name); putchar('('); print_address(stdout, &server->address); printf(") for %s:%s\n", trans->domain, stattype); } } } /* Update per domain statistics */ if (local_stat.ignore > 0) { if (verbose_level > 0) printf("%s:ignore\n", trans->domain); increment_entry(&domain_stat.ignore); err_count++; } if (local_stat.nxdomain > 0) { if (verbose_level > 0) printf("%s:nxdomain\n", trans->domain); increment_entry(&domain_stat.nxdomain); err_count++; } if (local_stat.othererr > 0) { if (verbose_level > 0) printf("%s:othererr\n", trans->domain); increment_entry(&domain_stat.othererr); err_count++; } if (local_stat.multiplesoa > 0) { if (verbose_level > 0) printf("%s:multiplesoa\n", trans->domain); increment_entry(&domain_stat.multiplesoa); err_count++; } if (local_stat.multiplecname > 0) { if (verbose_level > 0) printf("%s:multiplecname\n", trans->domain); increment_entry(&domain_stat.multiplecname); err_count++; } if (local_stat.brokenanswer > 0) { if (verbose_level > 0) printf("%s:brokenanswer\n", trans->domain); increment_entry(&domain_stat.brokenanswer); err_count++; } if (local_stat.lame > 0) { if (verbose_level > 0) printf("%s:lame\n", trans->domain); increment_entry(&domain_stat.lame); err_count++; } if (err_count > 1) increment_entry(&multiple_error_domains); /* * We regard the domain as valid if and only if no authoritative server * has a problem and at least one server is known to be valid. */ if (local_stat.valid > 0 && err_count == 0) { if (verbose_level > 1) printf("%s:valid\n", trans->domain); increment_entry(&domain_stat.valid); } /* * If the domain has no available server or all servers have the * 'unknown' result, the domain's result is also regarded as unknown. */ if (local_stat.valid == 0 && err_count == 0) { if (verbose_level > 1) printf("%s:unknown\n", trans->domain); increment_entry(&domain_stat.unknown); } } /* * Search for an existent name with an A RR */ static isc_result_t set_nextqname(struct probe_trans *trans) { isc_result_t result; size_t domainlen; isc_buffer_t b; char buf[4096]; /* XXX ad-hoc constant, but should be enough */ if (*trans->qlabel == NULL) return (ISC_R_NOMORE); result = isc_string_copy(buf, sizeof(buf), *trans->qlabel); if (result != ISC_R_SUCCESS) return (result); result = isc_string_append(buf, sizeof(buf), trans->domain); if (result != ISC_R_SUCCESS) return (result); domainlen = strlen(buf); isc_buffer_init(&b, buf, domainlen); isc_buffer_add(&b, domainlen); dns_fixedname_init(&trans->fixedname); trans->qname = dns_fixedname_name(&trans->fixedname); result = dns_name_fromtext(trans->qname, &b, dns_rootname, 0, NULL); trans->qlabel++; return (result); } static void request_done(isc_task_t *task, isc_event_t *event) { struct probe_trans *trans = event->ev_arg; dns_clientreqevent_t *rev = (dns_clientreqevent_t *)event; dns_message_t *rmessage; struct probe_ns *pns; struct server *server; isc_result_t result; query_result_t *resultp; dns_name_t *name; dns_rdataset_t *rdataset; dns_rdatatype_t type; REQUIRE(task == probe_task); REQUIRE(trans != NULL && trans->inuse == ISC_TRUE); rmessage = rev->rmessage; REQUIRE(rmessage == trans->rmessage); INSIST(outstanding_probes > 0); server = trans->current_ns->current_server; INSIST(server != NULL); if (server->result_a == none) { type = dns_rdatatype_a; resultp = &server->result_a; } else { resultp = &server->result_aaaa; type = dns_rdatatype_aaaa; } if (rev->result == ISC_R_SUCCESS) { if ((rmessage->flags & DNS_MESSAGEFLAG_AA) == 0) *resultp = lame; else if (rmessage->rcode == dns_rcode_nxdomain) *resultp = nxdomain; else if (rmessage->rcode != dns_rcode_noerror) *resultp = othererr; else if (rmessage->counts[DNS_SECTION_ANSWER] == 0) { /* no error but empty answer */ *resultp = notype; } else { result = dns_message_firstname(rmessage, DNS_SECTION_ANSWER); while (result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(rmessage, DNS_SECTION_ANSWER, &name); for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { (void)print_rdataset(rdataset, name); if (rdataset->type == dns_rdatatype_cname || rdataset->type == dns_rdatatype_dname) { /* Should chase the chain? */ *resultp = exist; goto found; } else if (rdataset->type == type) { *resultp = exist; goto found; } } result = dns_message_nextname(rmessage, DNS_SECTION_ANSWER); } /* * Something unexpected happened: the response * contained a non-empty authoritative answer, but we * could not find an expected result. */ *resultp = unexpected; } } else if (rev->result == DNS_R_RECOVERABLE || rev->result == DNS_R_BADLABELTYPE) { /* Broken response. Try identifying known cases. */ *resultp = brokenanswer; if (rmessage->counts[DNS_SECTION_ANSWER] > 0) { result = dns_message_firstname(rmessage, DNS_SECTION_ANSWER); while (result == ISC_R_SUCCESS) { /* * Check to see if the response has multiple * CNAME RRs. Update the result code if so. */ name = NULL; dns_message_currentname(rmessage, DNS_SECTION_ANSWER, &name); for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { if (rdataset->type == dns_rdatatype_cname && dns_rdataset_count(rdataset) > 1) { *resultp = multiplecname; goto found; } } result = dns_message_nextname(rmessage, DNS_SECTION_ANSWER); } } if (rmessage->counts[DNS_SECTION_AUTHORITY] > 0) { result = dns_message_firstname(rmessage, DNS_SECTION_AUTHORITY); while (result == ISC_R_SUCCESS) { /* * Check to see if the response has multiple * SOA RRs. Update the result code if so. */ name = NULL; dns_message_currentname(rmessage, DNS_SECTION_AUTHORITY, &name); for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { if (rdataset->type == dns_rdatatype_soa && dns_rdataset_count(rdataset) > 1) { *resultp = multiplesoa; goto found; } } result = dns_message_nextname(rmessage, DNS_SECTION_AUTHORITY); } } } else if (rev->result == ISC_R_TIMEDOUT) *resultp = timedout; else { fprintf(stderr, "unexpected result: %d (domain=%s, server=", rev->result, trans->domain); print_address(stderr, &server->address); fputc('\n', stderr); *resultp = unexpected; } found: INSIST(*resultp != none); if (type == dns_rdatatype_a && *resultp == exist) trans->qname_found = ISC_TRUE; dns_client_destroyreqtrans(&trans->reqid); isc_event_free(&event); dns_message_reset(trans->rmessage, DNS_MESSAGE_INTENTPARSE); result = probe_name(trans, type); if (result == ISC_R_NOMORE) { /* We've tried all addresses of all servers. */ if (type == dns_rdatatype_a && trans->qname_found) { /* * If we've explored A RRs and found an existent * record, we can move to AAAA. */ trans->current_ns = ISC_LIST_HEAD(trans->nslist); probe_name(trans, dns_rdatatype_aaaa); result = ISC_R_SUCCESS; } else if (type == dns_rdatatype_a) { /* * No server provided an existent A RR of this name. * Try next label. */ dns_fixedname_invalidate(&trans->fixedname); trans->qname = NULL; result = set_nextqname(trans); if (result == ISC_R_SUCCESS) { trans->current_ns = ISC_LIST_HEAD(trans->nslist); for (pns = trans->current_ns; pns != NULL; pns = ISC_LIST_NEXT(pns, link)) { for (server = ISC_LIST_HEAD(pns->servers); server != NULL; server = ISC_LIST_NEXT(server, link)) { INSIST(server->result_aaaa == none); server->result_a = none; } } result = probe_name(trans, dns_rdatatype_a); } } if (result != ISC_R_SUCCESS) { /* * We've explored AAAA RRs or failed to find a valid * query label. Wrap up the result and move to the * next domain. */ reset_probe(trans); } } else if (result != ISC_R_SUCCESS) reset_probe(trans); /* XXX */ } static isc_result_t probe_name(struct probe_trans *trans, dns_rdatatype_t type) { isc_result_t result; struct probe_ns *pns; struct server *server; REQUIRE(trans->reqid == NULL); REQUIRE(type == dns_rdatatype_a || type == dns_rdatatype_aaaa); for (pns = trans->current_ns; pns != NULL; pns = ISC_LIST_NEXT(pns, link)) { for (server = ISC_LIST_HEAD(pns->servers); server != NULL; server = ISC_LIST_NEXT(server, link)) { if ((type == dns_rdatatype_a && server->result_a == none) || (type == dns_rdatatype_aaaa && server->result_aaaa == none)) { pns->current_server = server; goto found; } } } found: trans->current_ns = pns; if (pns == NULL) return (ISC_R_NOMORE); INSIST(pns->current_server != NULL); dns_message_reset(trans->qmessage, DNS_MESSAGE_INTENTRENDER); result = make_querymessage(trans->qmessage, trans->qname, type); if (result != ISC_R_SUCCESS) return (result); result = dns_client_startrequest(client, trans->qmessage, trans->rmessage, &pns->current_server->address, 0, DNS_MESSAGEPARSE_BESTEFFORT, NULL, 120, 0, 4, probe_task, request_done, trans, &trans->reqid); return (result); } /* * Get IP addresses of NSes */ static void resolve_nsaddress(isc_task_t *task, isc_event_t *event) { struct probe_trans *trans = event->ev_arg; dns_clientresevent_t *rev = (dns_clientresevent_t *)event; dns_name_t *name; dns_rdataset_t *rdataset; dns_rdata_t rdata = DNS_RDATA_INIT; struct probe_ns *pns = trans->current_ns; isc_result_t result; REQUIRE(task == probe_task); REQUIRE(trans->inuse == ISC_TRUE); REQUIRE(pns != NULL); INSIST(outstanding_probes > 0); for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL; name = ISC_LIST_NEXT(name, link)) { for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { (void)print_rdataset(rdataset, name); if (rdataset->type != dns_rdatatype_a) continue; for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(rdataset)) { dns_rdata_in_a_t rdata_a; struct server *server; dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &rdata_a, NULL); if (result != ISC_R_SUCCESS) continue; server = isc_mem_get(mctx, sizeof(*server)); if (server == NULL) { fprintf(stderr, "resolve_nsaddress: " "mem_get failed"); result = ISC_R_NOMEMORY; POST(result); goto cleanup; } isc_sockaddr_fromin(&server->address, &rdata_a.in_addr, 53); ISC_LINK_INIT(server, link); server->result_a = none; server->result_aaaa = none; ISC_LIST_APPEND(pns->servers, server, link); } } } cleanup: dns_client_freeresanswer(client, &rev->answerlist); dns_client_destroyrestrans(&trans->resid); isc_event_free(&event); next_ns: trans->current_ns = ISC_LIST_NEXT(pns, link); if (trans->current_ns == NULL) { trans->current_ns = ISC_LIST_HEAD(trans->nslist); dns_fixedname_invalidate(&trans->fixedname); trans->qname = NULL; result = set_nextqname(trans); if (result == ISC_R_SUCCESS) result = probe_name(trans, dns_rdatatype_a); } else { result = fetch_nsaddress(trans); if (result != ISC_R_SUCCESS) goto next_ns; /* XXX: this is unlikely to succeed */ } if (result != ISC_R_SUCCESS) reset_probe(trans); } static isc_result_t fetch_nsaddress(struct probe_trans *trans) { struct probe_ns *pns; pns = trans->current_ns; REQUIRE(pns != NULL); return (dns_client_startresolve(client, pns->name, dns_rdataclass_in, dns_rdatatype_a, 0, probe_task, resolve_nsaddress, trans, &trans->resid)); } /* * Get NS RRset for a given domain */ static void reset_probe(struct probe_trans *trans) { struct probe_ns *pns; struct server *server; isc_result_t result; REQUIRE(trans->resid == NULL); REQUIRE(trans->reqid == NULL); update_stat(trans); dns_message_reset(trans->qmessage, DNS_MESSAGE_INTENTRENDER); dns_message_reset(trans->rmessage, DNS_MESSAGE_INTENTPARSE); trans->inuse = ISC_FALSE; if (trans->domain != NULL) isc_mem_free(mctx, trans->domain); trans->domain = NULL; if (trans->qname != NULL) dns_fixedname_invalidate(&trans->fixedname); trans->qname = NULL; trans->qlabel = qlabels; trans->qname_found = ISC_FALSE; trans->current_ns = NULL; while ((pns = ISC_LIST_HEAD(trans->nslist)) != NULL) { ISC_LIST_UNLINK(trans->nslist, pns, link); while ((server = ISC_LIST_HEAD(pns->servers)) != NULL) { ISC_LIST_UNLINK(pns->servers, server, link); isc_mem_put(mctx, server, sizeof(*server)); } isc_mem_put(mctx, pns, sizeof(*pns)); } outstanding_probes--; result = probe_domain(trans); if (result == ISC_R_NOMORE && outstanding_probes == 0) isc_app_ctxshutdown(actx); } static void resolve_ns(isc_task_t *task, isc_event_t *event) { struct probe_trans *trans = event->ev_arg; dns_clientresevent_t *rev = (dns_clientresevent_t *)event; dns_name_t *name; dns_rdataset_t *rdataset; isc_result_t result = ISC_R_SUCCESS; dns_rdata_t rdata = DNS_RDATA_INIT; struct probe_ns *pns; REQUIRE(task == probe_task); REQUIRE(trans->inuse == ISC_TRUE); INSIST(outstanding_probes > 0); for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL; name = ISC_LIST_NEXT(name, link)) { for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { (void)print_rdataset(rdataset, name); if (rdataset->type != dns_rdatatype_ns) continue; for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(rdataset)) { dns_rdata_ns_t ns; dns_rdataset_current(rdataset, &rdata); /* * Extract the name from the NS record. */ result = dns_rdata_tostruct(&rdata, &ns, NULL); if (result != ISC_R_SUCCESS) continue; pns = isc_mem_get(mctx, sizeof(*pns)); if (pns == NULL) { fprintf(stderr, "resolve_ns: mem_get failed"); result = ISC_R_NOMEMORY; POST(result); /* * XXX: should we continue with the * available servers anyway? */ goto cleanup; } dns_fixedname_init(&pns->fixedname); pns->name = dns_fixedname_name(&pns->fixedname); ISC_LINK_INIT(pns, link); ISC_LIST_APPEND(trans->nslist, pns, link); ISC_LIST_INIT(pns->servers); dns_name_copy(&ns.name, pns->name, NULL); dns_rdata_reset(&rdata); dns_rdata_freestruct(&ns); } } } cleanup: dns_client_freeresanswer(client, &rev->answerlist); dns_client_destroyrestrans(&trans->resid); isc_event_free(&event); if (!ISC_LIST_EMPTY(trans->nslist)) { /* Go get addresses of NSes */ trans->current_ns = ISC_LIST_HEAD(trans->nslist); result = fetch_nsaddress(trans); } else result = ISC_R_FAILURE; if (result == ISC_R_SUCCESS) return; reset_probe(trans); } static isc_result_t probe_domain(struct probe_trans *trans) { isc_result_t result; size_t domainlen; isc_buffer_t b; char buf[4096]; /* XXX ad hoc constant, but should be enough */ char *cp; REQUIRE(trans != NULL); REQUIRE(trans->inuse == ISC_FALSE); REQUIRE(outstanding_probes < MAX_PROBES); /* Construct domain */ cp = fgets(buf, sizeof(buf), fp); if (cp == NULL) return (ISC_R_NOMORE); if ((cp = strchr(buf, '\n')) != NULL) /* zap NL if any */ *cp = '\0'; trans->domain = isc_mem_strdup(mctx, buf); if (trans->domain == NULL) { fprintf(stderr, "failed to allocate memory for domain: %s", cp); return (ISC_R_NOMEMORY); } /* Start getting NS for the domain */ domainlen = strlen(buf); isc_buffer_init(&b, buf, domainlen); isc_buffer_add(&b, domainlen); dns_fixedname_init(&trans->fixedname); trans->qname = dns_fixedname_name(&trans->fixedname); result = dns_name_fromtext(trans->qname, &b, dns_rootname, 0, NULL); if (result != ISC_R_SUCCESS) goto cleanup; result = dns_client_startresolve(client, trans->qname, dns_rdataclass_in, dns_rdatatype_ns, 0, probe_task, resolve_ns, trans, &trans->resid); if (result != ISC_R_SUCCESS) goto cleanup; trans->inuse = ISC_TRUE; outstanding_probes++; return (ISC_R_SUCCESS); cleanup: isc_mem_free(mctx, trans->domain); dns_fixedname_invalidate(&trans->fixedname); return (result); } ISC_PLATFORM_NORETURN_PRE static void usage(void) ISC_PLATFORM_NORETURN_POST; static void usage(void) { fprintf(stderr, "usage: nsprobe [-d] [-v [-v...]] [-c cache_address] " "[input_file]\n"); exit(1); } int main(int argc, char *argv[]) { int i, ch, error; struct addrinfo hints, *res; isc_result_t result; isc_sockaddr_t sa; isc_sockaddrlist_t servers; isc_taskmgr_t *taskmgr = NULL; isc_socketmgr_t *socketmgr = NULL; isc_timermgr_t *timermgr = NULL; while ((ch = getopt(argc, argv, "c:dhv")) != -1) { switch (ch) { case 'c': cacheserver = optarg; break; case 'd': debug_mode = ISC_TRUE; break; case 'h': usage(); break; case 'v': verbose_level++; break; default: usage(); break; } } argc -= optind; argv += optind; /* Common set up */ isc_lib_register(); result = dns_lib_init(); if (result != ISC_R_SUCCESS) { fprintf(stderr, "dns_lib_init failed: %d\n", result); exit(1); } result = ctxs_init(&mctx, &actx, &taskmgr, &socketmgr, &timermgr); if (result != ISC_R_SUCCESS) { fprintf(stderr, "ctx create failed: %d\n", result); exit(1); } isc_app_ctxstart(actx); result = dns_client_createx(mctx, actx, taskmgr, socketmgr, timermgr, 0, &client); if (result != ISC_R_SUCCESS) { fprintf(stderr, "dns_client_createx failed: %d\n", result); exit(1); } /* Set local cache server */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; error = getaddrinfo(cacheserver, "53", &hints, &res); if (error != 0) { fprintf(stderr, "failed to convert server name (%s): %s\n", cacheserver, gai_strerror(error)); exit(1); } if (res->ai_addrlen > sizeof(sa.type)) { fprintf(stderr, "assumption failure: addrlen is too long: %ld\n", (long)res->ai_addrlen); exit(1); } memcpy(&sa.type.sa, res->ai_addr, res->ai_addrlen); sa.length = res->ai_addrlen; freeaddrinfo(res); ISC_LINK_INIT(&sa, link); ISC_LIST_INIT(servers); ISC_LIST_APPEND(servers, &sa, link); result = dns_client_setservers(client, dns_rdataclass_in, NULL, &servers); if (result != ISC_R_SUCCESS) { fprintf(stderr, "failed to set server: %d\n", result); exit(1); } /* Create the main task */ probe_task = NULL; result = isc_task_create(taskmgr, 0, &probe_task); if (result != ISC_R_SUCCESS) { fprintf(stderr, "failed to create task: %d\n", result); exit(1); } /* Open input file */ if (argc == 0) fp = stdin; else { fp = fopen(argv[0], "r"); if (fp == NULL) { fprintf(stderr, "failed to open input file: %s\n", argv[0]); exit(1); } } /* Set up and start probe */ for (i = 0; i < MAX_PROBES; i++) { probes[i].inuse = ISC_FALSE; probes[i].domain = NULL; dns_fixedname_init(&probes[i].fixedname); probes[i].qname = NULL; probes[i].qlabel = qlabels; probes[i].qname_found = ISC_FALSE; probes[i].resid = NULL; ISC_LIST_INIT(probes[i].nslist); probes[i].reqid = NULL; probes[i].qmessage = NULL; result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &probes[i].qmessage); if (result == ISC_R_SUCCESS) { result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &probes[i].rmessage); } if (result != ISC_R_SUCCESS) { fprintf(stderr, "initialization failure\n"); exit(1); } } for (i = 0; i < MAX_PROBES; i++) { result = probe_domain(&probes[i]); if (result == ISC_R_NOMORE) break; else if (result != ISC_R_SUCCESS) { fprintf(stderr, "failed to issue an initial probe\n"); exit(1); } } /* Start event loop */ isc_app_ctxrun(actx); /* Dump results */ printf("Per domain results (out of %lu domains):\n", number_of_domains); printf(" valid: %lu\n" " ignore: %lu\n" " nxdomain: %lu\n" " othererr: %lu\n" " multiplesoa: %lu\n" " multiplecname: %lu\n" " brokenanswer: %lu\n" " lame: %lu\n" " unknown: %lu\n" " multiple errors: %lu\n", domain_stat.valid, domain_stat.ignore, domain_stat.nxdomain, domain_stat.othererr, domain_stat.multiplesoa, domain_stat.multiplecname, domain_stat.brokenanswer, domain_stat.lame, domain_stat.unknown, multiple_error_domains); printf("Per server results (out of %lu servers):\n", number_of_servers); printf(" valid: %lu\n" " ignore: %lu\n" " nxdomain: %lu\n" " othererr: %lu\n" " multiplesoa: %lu\n" " multiplecname: %lu\n" " brokenanswer: %lu\n" " lame: %lu\n" " unknown: %lu\n", server_stat.valid, server_stat.ignore, server_stat.nxdomain, server_stat.othererr, server_stat.multiplesoa, server_stat.multiplecname, server_stat.brokenanswer, server_stat.lame, server_stat.unknown); /* Cleanup */ for (i = 0; i < MAX_PROBES; i++) { dns_message_destroy(&probes[i].qmessage); dns_message_destroy(&probes[i].rmessage); } isc_task_detach(&probe_task); dns_client_destroy(&client); dns_lib_shutdown(); isc_app_ctxfinish(actx); ctxs_destroy(&mctx, &actx, &taskmgr, &socketmgr, &timermgr); exit(0); }