Commit b6e72599 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[3391] Fix distcheck issues.

parent 250f56b4
......@@ -1469,7 +1469,6 @@ AC_CONFIG_FILES([compatcheck/Makefile
src/lib/dhcpsrv/tests/Makefile
src/lib/dhcpsrv/tests/test_libraries.h
src/lib/dhcp/tests/Makefile
src/lib/dns/benchmarks/Makefile
src/lib/dns/gen-rdatacode.py
src/lib/dns/Makefile
src/lib/dns/python/Makefile
......@@ -1668,11 +1667,6 @@ fi
cat >> config.report << END
Components:
DHCP: $want_dhcp
DNS: $want_dns
Experimental resolver: $want_experimental_resolver
Features:
$enable_features
......
......@@ -15,7 +15,9 @@
#ifndef ASIOLINK_DNS_SERVICE_H
#define ASIOLINK_DNS_SERVICE_H 1
#include <resolve/resolver_interface.h>
// The commented header occurs to be unused so we remove it to get
// rid of dependency on resolver.
// #include <resolve/resolver_interface.h>
#include <asiolink/io_service.h>
#include <asiolink/simple_callback.h>
......
/resolve_messages.cc
/resolve_messages.h
/s-messages
SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
# Define rule to build logging source files from message file
resolve_messages.h resolve_messages.cc: s-messages
s-messages: resolve_messages.mes
$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/resolve/resolve_messages.mes
touch $@
# Tell Automake that the nsasdef.{cc,h} source files are created in the build
# process, so it must create these before doing anything else. Although they
# are a dependency of the library (so will be created from the message file
# anyway), there is no guarantee as to exactly _when_ in the build they will be
# created. As the .h file is included in other sources file (so must be
# present when they are compiled), the safest option is to create it first.
BUILT_SOURCES = resolve_messages.h resolve_messages.cc
CLEANFILES = *.gcno *.gcda resolve_messages.cc resolve_messages.h s-messages
lib_LTLIBRARIES = libb10-resolve.la
libb10_resolve_la_SOURCES = resolve.h resolve.cc
libb10_resolve_la_SOURCES += resolve_log.h resolve_log.cc
libb10_resolve_la_SOURCES += resolver_interface.h
libb10_resolve_la_SOURCES += resolver_callback.h resolver_callback.cc
libb10_resolve_la_SOURCES += response_classifier.cc response_classifier.h
libb10_resolve_la_SOURCES += recursive_query.cc recursive_query.h
nodist_libb10_resolve_la_SOURCES = resolve_messages.h resolve_messages.cc
libb10_resolve_la_LIBADD = $(top_builddir)/src/lib/dns/libb10-dns++.la
libb10_resolve_la_LIBADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
libb10_resolve_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
libb10_resolve_la_LIBADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
libb10_resolve_la_LIBADD += $(top_builddir)/src/lib/nsas/libb10-nsas.la
# The message file should be in the distribution.
EXTRA_DIST = resolve_messages.mes
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
libb10_resolve_la_CXXFLAGS = $(AM_CXXFLAGS)
libb10_resolve_la_CPPFLAGS = $(AM_CPPFLAGS)
// Copyright (C) 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.
#include <config.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h> // for some IPC/network system calls
#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>
#include <dns/question.h>
#include <dns/message.h>
#include <dns/opcode.h>
#include <dns/exceptions.h>
#include <dns/rdataclass.h>
#include <resolve/resolve.h>
#include <resolve/resolve_log.h>
#include <resolve/resolve_messages.h>
#include <cache/resolver_cache.h>
#include <nsas/address_request_callback.h>
#include <nsas/nameserver_address.h>
#include <asio.hpp>
#include <asiodns/dns_service.h>
#include <asiodns/io_fetch.h>
#include <asiolink/io_service.h>
#include <resolve/response_classifier.h>
#include <resolve/recursive_query.h>
using namespace isc::dns;
using namespace isc::nsas;
using namespace isc::util;
using namespace isc::asiolink;
using namespace isc::resolve;
namespace isc {
namespace asiodns {
namespace {
// Function to check if the given name/class has any address in the cache
bool
hasAddress(const Name& name, const RRClass& rrClass,
const isc::cache::ResolverCache& cache)
{
// FIXME: If we are single-stack and we get only the other type of
// address, what should we do? In that case, it will be considered
// unreachable, which is most probably true, because A and AAAA will
// usually have the same RTT, so we should have both or none from the
// glue.
return (cache.lookup(name, RRType::A(), rrClass) != RRsetPtr() ||
cache.lookup(name, RRType::AAAA(), rrClass) != RRsetPtr());
}
// Convenience function for debug messages. Question::toText() includes
// a trailing newline in its output, which makes it awkward to embed in a
// message. This just strips that newline from it.
std::string
questionText(const isc::dns::Question& question) {
std::string text = question.toText();
if (!text.empty()) {
text.erase(text.size() - 1);
}
return (text);
}
} // anonymous namespace
/// \brief Find deepest usable delegation in the cache
///
/// This finds the deepest delegation we have in cache and is safe to use.
/// It is not public function, therefore it's not in header. But it's not
/// in anonymous namespace, so we can call it from unittests.
/// \param name The name we want to delegate to.
/// \param rrclass The class.
/// \param cache The place too look for known delegations.
std::string
deepestDelegation(Name name, RRClass rrclass,
isc::cache::ResolverCache& cache)
{
RRsetPtr cachedNS;
// Look for delegation point from bottom, until we find one with
// IP address or get to root.
//
// We need delegation with IP address so we can ask it right away.
// If we don't have the IP address, we would need to ask above it
// anyway in the best case, and the NS could be inside the zone,
// and we could get all loopy with the NSAS in the worst case.
while (name.getLabelCount() > 1 &&
(cachedNS = cache.lookupDeepestNS(name, rrclass)) != RRsetPtr()) {
// Look if we have an IP address for the NS
for (RdataIteratorPtr ns(cachedNS->getRdataIterator());
!ns->isLast(); ns->next()) {
// Do we have IP for this specific NS?
if (hasAddress(dynamic_cast<const rdata::generic::NS&>(
ns->getCurrent()).getNSName(), rrclass,
cache)) {
// Found one, stop checking and use this zone
// (there may be more addresses, that's only better)
return (cachedNS->getName().toText());
}
}
// We don't have anything for this one, so try something higher
if (name.getLabelCount() > 1) {
name = name.split(1);
}
}
// Fallback, nothing found, start at root
return (".");
}
// Here we do not use the typedef above, as the SunStudio compiler
// mishandles this in its name mangling, and wouldn't compile.
// We can probably use a typedef, but need to move it to a central
// location and use it consistently.
RecursiveQuery::RecursiveQuery(DNSServiceBase& dns_service,
isc::nsas::NameserverAddressStore& nsas,
isc::cache::ResolverCache& cache,
const std::vector<std::pair<std::string, uint16_t> >& upstream,
const std::vector<std::pair<std::string, uint16_t> >& upstream_root,
int query_timeout, int client_timeout, int lookup_timeout,
unsigned retries)
:
dns_service_(dns_service),
nsas_(nsas), cache_(cache),
upstream_(new AddressVector(upstream)),
upstream_root_(new AddressVector(upstream_root)),
test_server_("", 0),
query_timeout_(query_timeout), client_timeout_(client_timeout),
lookup_timeout_(lookup_timeout), retries_(retries), rtt_recorder_()
{
}
// Set the test server - only used for unit testing.
void
RecursiveQuery::setTestServer(const std::string& address, uint16_t port) {
LOG_WARN(isc::resolve::logger, RESLIB_TEST_SERVER).arg(address).arg(port);
test_server_.first = address;
test_server_.second = port;
}
// Set the RTT recorder - only used for testing
void
RecursiveQuery::setRttRecorder(boost::shared_ptr<RttRecorder>& recorder) {
rtt_recorder_ = recorder;
}
namespace {
typedef std::pair<std::string, uint16_t> addr_t;
/*
* This is a query in progress. When a new query is made, this one holds
* the context information about it, like how many times we are allowed
* to retry on failure, what to do when we succeed, etc.
*
* Used by RecursiveQuery::sendQuery.
*/
class RunningQuery : public IOFetch::Callback, public AbstractRunningQuery {
class ResolverNSASCallback : public isc::nsas::AddressRequestCallback {
public:
ResolverNSASCallback(RunningQuery* rq) : rq_(rq) {}
void success(const isc::nsas::NameserverAddress& address) {
// Success callback, send query to found namesever
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQ_SUCCESS)
.arg(address.getAddress().toText());
rq_->nsasCallbackCalled();
rq_->sendTo(address);
}
void unreachable() {
// Nameservers unreachable: drop query or send servfail?
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQ_FAIL);
rq_->nsasCallbackCalled();
rq_->makeSERVFAIL();
rq_->callCallback(true);
rq_->stop();
}
private:
RunningQuery* rq_;
};
private:
// The io service to handle async calls
IOService& io_;
// Info for (re)sending the query (the question and destination)
Question question_;
// This is the query message got from client
ConstMessagePtr query_message_;
// This is where we build and store our final answer
MessagePtr answer_message_;
// Test server - only used for testing. This takes precedence over all
// other servers if the port is non-zero.
std::pair<std::string, uint16_t> test_server_;
// Buffer to store the intermediate results.
OutputBufferPtr buffer_;
// The callback will be called when we have either decided we
// are done, or when we give up
isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
// Protocol used for the last query. This is set to IOFetch::UDP when a
// new upstream query is initiated, and changed to IOFetch::TCP if a
// packet is returned with the TC bit set. It is stored here to detect the
// case of a TCP packet being returned with the TC bit set.
IOFetch::Protocol protocol_;
// EDNS flag
bool edns_;
// To prevent both unreasonably long cname chains and cname loops,
// we simply keep a counter of the number of CNAMEs we have
// followed so far (and error if it exceeds RESOLVER_MAX_CNAME_CHAIN
// from lib/resolve/response_classifier.h)
unsigned cname_count_;
/*
* TODO Do something more clever with timeouts. In the long term, some
* computation of average RTT, increase with each retry, etc.
*/
// Timeout information for outgoing queries
int query_timeout_;
unsigned retries_;
// normal query state
// TODO: replace by our wrapper
asio::deadline_timer client_timer;
asio::deadline_timer lookup_timer;
// If we timed out ourselves (lookup timeout), stop issuing queries
bool done_;
// If we have a client timeout, we call back with a failure message,
// but we do not stop yet. We use this variable to make sure we
// don't call back a second time later
bool callback_called_;
// Reference to our NSAS
isc::nsas::NameserverAddressStore& nsas_;
// Reference to our cache
isc::cache::ResolverCache& cache_;
// the 'current' zone we are in (i.e.) we start out at the root,
// and for each delegation this gets updated with the zone the
// delegation points to.
// TODO: make this a Name (it is a string right now because most
// of the call we use it in take a string, we need update those
// too).
std::string cur_zone_;
// This is the handler we pass on to the NSAS; it is called when
// the NSAS has an address for us to query
boost::shared_ptr<ResolverNSASCallback> nsas_callback_;
// this is set to true if we have asked the nsas to give us
// an address and we are waiting for it to call us back.
// We use is to cancel the outstanding callback in case we
// have a lookup timeout and decide to give up
bool nsas_callback_out_;
// This is the nameserver we have an outstanding query to.
// It is used to update the RTT once the query returns
isc::nsas::NameserverAddress current_ns_address;
// The moment in time we sent a query to the nameserver above.
struct timeval current_ns_qsent_time;
// RunningQuery deletes itself when it is done. In order for us
// to do this safely, we must make sure that there are no events
// that might call back to it. There are two types of events in
// this sense; the timers we set ourselves (lookup and client),
// and outstanding queries to nameservers. When each of these is
// started, we increase this value. When they fire, it is decreased
// again. We cannot delete ourselves until this value is back to 0.
//
// Note that the NSAS callback is *not* seen as an outstanding
// event; we can cancel the NSAS callback safely.
size_t outstanding_events_;
// RTT Recorder. Used for testing, the RTTs of queries are
// sent to this object as well as being used to update the NSAS.
boost::shared_ptr<RttRecorder> rtt_recorder_;
// perform a single lookup; first we check the cache to see
// if we have a response for our query stored already. if
// so, call handlerecursiveresponse(), if not, we call send()
void doLookup() {
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RUNQ_CACHE_LOOKUP)
.arg(questionText(question_));
Message cached_message(Message::RENDER);
isc::resolve::initResponseMessage(question_, cached_message);
if (cache_.lookup(question_.getName(), question_.getType(),
question_.getClass(), cached_message)) {
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RUNQ_CACHE_FIND)
.arg(questionText(question_));
// Should these be set by the cache too?
cached_message.setOpcode(Opcode::QUERY());
cached_message.setRcode(Rcode::NOERROR());
cached_message.setHeaderFlag(Message::HEADERFLAG_QR);
if (handleRecursiveAnswer(cached_message)) {
callCallback(true);
stop();
}
} else {
cur_zone_ = deepestDelegation(question_.getName(),
question_.getClass(), cache_);
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_DEEPEST)
.arg(questionText(question_)).arg(cur_zone_);
send();
}
}
// Send the current question to the given nameserver address
void sendTo(const isc::nsas::NameserverAddress& address) {
// We need to keep track of the Address, so that we can update
// the RTT
current_ns_address = address;
gettimeofday(&current_ns_qsent_time, NULL);
++outstanding_events_;
if (test_server_.second != 0) {
IOFetch query(protocol_, io_, question_,
test_server_.first,
test_server_.second, buffer_, this,
query_timeout_, edns_);
io_.get_io_service().post(query);
} else {
IOFetch query(protocol_, io_, question_,
current_ns_address.getAddress(),
53, buffer_, this,
query_timeout_, edns_);
io_.get_io_service().post(query);
}
}
// 'general' send, ask the NSAS to give us an address.
void send(IOFetch::Protocol protocol = IOFetch::UDP, bool edns = true) {
protocol_ = protocol; // Store protocol being used for this
edns_ = edns;
if (test_server_.second != 0) {
// Send query to test server
LOG_DEBUG(isc::resolve::logger,
RESLIB_DBG_TRACE, RESLIB_TEST_UPSTREAM)
.arg(questionText(question_)).arg(test_server_.first);
gettimeofday(&current_ns_qsent_time, NULL);
++outstanding_events_;
IOFetch query(protocol, io_, question_,
test_server_.first,
test_server_.second, buffer_, this,
query_timeout_, edns_);
io_.get_io_service().post(query);
} else {
// Ask the NSAS for an address for the current zone,
// the callback will call the actual sendTo()
LOG_DEBUG(isc::resolve::logger,
RESLIB_DBG_TRACE, RESLIB_NSAS_LOOKUP)
.arg(cur_zone_);
// Can we have multiple calls to nsas_out? Let's assume not
// for now
assert(!nsas_callback_out_);
nsas_callback_out_ = true;
nsas_.lookup(cur_zone_, question_.getClass(), nsas_callback_);
}
}
// Called by our NSAS callback handler so we know we do not have
// an outstanding NSAS call anymore.
void nsasCallbackCalled() {
nsas_callback_out_ = false;
}
// This function is called by operator() and lookup();
// We have an answer either from a nameserver or the cache, and
// we do not know yet if this is a final answer we can send back or
// that more recursive processing needs to be done.
// Depending on the content, we go on recursing or return
//
// This method also updates the cache, depending on the content
// of the message
//
// returns true if we are done (either we have an answer or an
// error message)
// returns false if we are not done
bool handleRecursiveAnswer(const Message& incoming) {
// In case we get a CNAME, we store the target
// here (classify() will set it when it walks through
// the cname chain to verify it).
Name cname_target(question_.getName());
isc::resolve::ResponseClassifier::Category category =
isc::resolve::ResponseClassifier::classify(
question_, incoming, cname_target, cname_count_);
bool found_ns = false;
switch (category) {
case isc::resolve::ResponseClassifier::ANSWER:
case isc::resolve::ResponseClassifier::ANSWERCNAME:
// Answer received - copy and return.
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_ANSWER)
.arg(questionText(question_));
isc::resolve::copyResponseMessage(incoming, answer_message_);
cache_.update(*answer_message_);
return (true);
break;
case isc::resolve::ResponseClassifier::CNAME:
// CNAME received.
// (unfinished) CNAME. We set our question_ to the CNAME
// target, then start over at the beginning (for now, that
// is, we reset our 'current servers' to the root servers).
if (cname_count_ >= RESOLVER_MAX_CNAME_CHAIN) {
// CNAME chain too long - just give up
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_LONG_CHAIN)
.arg(questionText(question_));
makeSERVFAIL();
return (true);
}
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_CNAME)
.arg(questionText(question_));
answer_message_->appendSection(Message::SECTION_ANSWER,
incoming);
question_ = Question(cname_target, question_.getClass(),
question_.getType());
// Follow CNAME chain.
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_FOLLOW_CNAME)
.arg(questionText(question_));
doLookup();
return (false);
break;
case isc::resolve::ResponseClassifier::NXDOMAIN:
case isc::resolve::ResponseClassifier::NXRRSET:
// Received NXDOMAIN or NXRRSET, just copy and return
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_NXDOM_NXRR)
.arg(questionText(question_));
isc::resolve::copyResponseMessage(incoming, answer_message_);
// no negcache yet
//cache_.update(*answer_message_);
return (true);
break;
case isc::resolve::ResponseClassifier::REFERRAL:
// Response is a referral
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_REFERRAL)
.arg(questionText(question_));
cache_.update(incoming);
// Referral. For now we just take the first glue address
// we find and continue with that
// auth section should have at least one RRset
// and one of them should be an NS (otherwise
// classifier should have error'd) to a subdomain
for (RRsetIterator rrsi = incoming.beginSection(Message::SECTION_AUTHORITY);
rrsi != incoming.endSection(Message::SECTION_AUTHORITY) && !found_ns;
++rrsi) {
ConstRRsetPtr rrs = *rrsi;
if (rrs->getType() == RRType::NS()) {
NameComparisonResult compare(Name(cur_zone_).compare(rrs->getName()));
if (compare.getRelation() == NameComparisonResult::SUPERDOMAIN) {
// TODO: make cur_zone_ a Name instead of a string
// (this requires a few API changes in related
// libraries, so as not to need many conversions)
cur_zone_ = rrs->getName().toText();
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_REFER_ZONE)
.arg(cur_zone_);
found_ns = true;
break;
}
}
}
if (found_ns) {
// next resolver round
// we do NOT use doLookup() here, but send() (i.e. we
// skip the cache), since if we had the final answer
// instead of a delegation cached, we would have been
// there by now.
GlueHints glue_hints(cur_zone_, incoming);
// Ask the NSAS for an address, or glue.
// This will eventually result in either sendTo()
// or stop() being called by nsas_callback_
assert(!nsas_callback_out_);
nsas_callback_out_ = true;
nsas_.lookup(cur_zone_, question_.getClass(),
nsas_callback_, ANY_OK, glue_hints);
return (false);
} else {
// Referral was received but did not contain an NS RRset.
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS