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
cat >> config.report << END
-Components:
- DHCP: $want_dhcp
- DNS: $want_dns
- Experimental resolver: $want_experimental_resolver
-
Features:
$enable_features
#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>
+++ /dev/null
-/resolve_messages.cc
-/resolve_messages.h
-/s-messages
+++ /dev/null
-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)
+++ /dev/null
-// 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(¤t_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(¤t_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, RESLIB_NO_NS_RRSET)
- .arg(questionText(question_));
-
- // TODO this will result in answering with the delegation. oh well
- isc::resolve::copyResponseMessage(incoming, answer_message_);
- return (true);
- }
- break;
-
- case isc::resolve::ResponseClassifier::TRUNCATED:
- // Truncated packet. If the protocol we used for the last one is
- // UDP, re-query using TCP. Otherwise regard it as an error.
- if (protocol_ == IOFetch::UDP) {
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
- RESLIB_TRUNCATED).arg(questionText(question_));
- send(IOFetch::TCP);
- return (false);
- }
-
- // Was a TCP query so we have received a packet over TCP with the
- // TC bit set: report an error by going to the common
- // error code.
- goto SERVFAIL;
-
- case isc::resolve::ResponseClassifier::RCODE:
- // see if it's a FORMERR and a potential EDNS problem
- if (incoming.getRcode() == Rcode::FORMERR()) {
- if (protocol_ == IOFetch::UDP && edns_) {
- // TODO: in case we absolutely need EDNS (i.e. for DNSSEC
- // aware queries), we might want to try TCP before we give
- // up. For now, just try UDP, no EDNS
- send(IOFetch::UDP, false);
- return (false);
- }
-
- // TC should take care of non-EDNS over UDP, fall through to
- // SERVFAIL if we get FORMERR instead
- }
- goto SERVFAIL;
-
- default:
-SERVFAIL:
- // Some error in received packet it. Report it and return SERVFAIL
- // to the caller.
- if (logger.isDebugEnabled()) {
- reportResponseClassifierError(category, incoming.getRcode());
- }
- makeSERVFAIL();
- return (true);
- }
-
- // If we get here, there is some serious logic error (or a missing
- // "return").
- assert(false);
- return (true); // To keep the compiler happy
- }
-
- /// \brief Report classification-detected error
- ///
- /// When the response classifier has detected an error in the response from
- /// an upstream query, this method is called to log a debug message giving
- /// information about the problem.
- ///
- /// \param category Classification code for the packet
- /// \param rcode RCODE value in the packet
- void reportResponseClassifierError(ResponseClassifier::Category category,
- const Rcode& rcode)
- {
- // We could set up a table of response classifications to message
- // IDs here and index into that table. But given that (a) C++ does
- // not have C's named initializers, (b) the codes for the
- // response classifier are in another module and (c) not all messages
- // have the same number of arguments, the setup of the table would be
- // almost as long as the code here: it would need to include a number
- // of assertions to ensure that any change to the the response
- // classifier codes was detected, and the checking logic would need to
- // check that the numeric value of the code lay within the defined
- // limits of the table.
-
- if (category == ResponseClassifier::RCODE) {
-
- // Special case as this message takes two arguments.
- LOG_DEBUG(logger, RESLIB_DBG_RESULTS, RESLIB_RCODE_RETURNED).
- arg(questionText(question_)).arg(rcode);
-
- } else {
-
- isc::log::MessageID message_id;
- switch (category) {
- case ResponseClassifier::TRUNCATED:
- message_id = RESLIB_TCP_TRUNCATED;
- break;
-
- case ResponseClassifier::EMPTY:
- message_id = RESLIB_EMPTY_RESPONSE;
- break;
-
- case ResponseClassifier::EXTRADATA:
- message_id = RESLIB_EXTRADATA_RESPONSE;
- break;
-
- case ResponseClassifier::INVNAMCLASS:
- message_id = RESLIB_INVALID_NAMECLASS_RESPONSE;
- break;
-
- case ResponseClassifier::INVTYPE:
- message_id = RESLIB_INVALID_TYPE_RESPONSE;
- break;
-
- case ResponseClassifier::MISMATQUEST:
- message_id = RESLIB_INVALID_QNAME_RESPONSE;
- break;
-
- case ResponseClassifier::MULTICLASS:
- message_id = RESLIB_MULTIPLE_CLASS_RESPONSE;
- break;
-
- case ResponseClassifier::NOTONEQUEST:
- message_id = RESLIB_NOT_ONE_QNAME_RESPONSE;
- break;
-
- case ResponseClassifier::NOTRESPONSE:
- message_id = RESLIB_NOT_RESPONSE;
- break;
-
- case ResponseClassifier::NOTSINGLE:
- message_id = RESLIB_NOTSINGLE_RESPONSE;
- break;
-
- case ResponseClassifier::OPCODE:
- message_id = RESLIB_OPCODE_RESPONSE;
- break;
-
- default:
- message_id = RESLIB_ERROR_RESPONSE;
- break;
- }
- LOG_DEBUG(logger, RESLIB_DBG_RESULTS, message_id).
- arg(questionText(question_));
- }
- }
-
-public:
- RunningQuery(IOService& io,
- const Question& question,
- MessagePtr answer_message,
- std::pair<std::string, uint16_t>& test_server,
- OutputBufferPtr buffer,
- isc::resolve::ResolverInterface::CallbackPtr cb,
- int query_timeout, int client_timeout, int lookup_timeout,
- unsigned retries,
- isc::nsas::NameserverAddressStore& nsas,
- isc::cache::ResolverCache& cache,
- boost::shared_ptr<RttRecorder>& recorder)
- :
- io_(io),
- question_(question),
- query_message_(),
- answer_message_(answer_message),
- test_server_(test_server),
- buffer_(buffer),
- resolvercallback_(cb),
- protocol_(IOFetch::UDP),
- cname_count_(0),
- query_timeout_(query_timeout),
- retries_(retries),
- client_timer(io.get_io_service()),
- lookup_timer(io.get_io_service()),
- done_(false),
- callback_called_(false),
- nsas_(nsas),
- cache_(cache),
- cur_zone_("."),
- nsas_callback_(),
- nsas_callback_out_(false),
- outstanding_events_(0),
- rtt_recorder_(recorder)
- {
- // Set here to avoid using "this" in initializer list.
- nsas_callback_.reset(new ResolverNSASCallback(this));
-
- // Setup the timer to stop trying (lookup_timeout)
- if (lookup_timeout >= 0) {
- lookup_timer.expires_from_now(
- boost::posix_time::milliseconds(lookup_timeout));
- ++outstanding_events_;
- lookup_timer.async_wait(boost::bind(&RunningQuery::lookupTimeout, this));
- }
-
- // Setup the timer to send an answer (client_timeout)
- if (client_timeout >= 0) {
- client_timer.expires_from_now(
- boost::posix_time::milliseconds(client_timeout));
- ++outstanding_events_;
- client_timer.async_wait(boost::bind(&RunningQuery::clientTimeout, this));
- }
-
- doLookup();
- }
-
- virtual ~RunningQuery() {};
-
- // called if we have a lookup timeout; if our callback has
- // not been called, call it now. Then stop.
- void lookupTimeout() {
- if (!callback_called_) {
- makeSERVFAIL();
- callCallback(true);
- }
- assert(outstanding_events_ > 0);
- --outstanding_events_;
- stop();
- }
-
- // called if we have a client timeout; if our callback has
- // not been called, call it now. But do not stop.
- void clientTimeout() {
- if (!callback_called_) {
- makeSERVFAIL();
- callCallback(true);
- }
- assert(outstanding_events_ > 0);
- --outstanding_events_;
- if (outstanding_events_ == 0) {
- stop();
- }
- }
-
- // If the callback has not been called yet, call it now
- // If success is true, we call 'success' with our answer_message
- // If it is false, we call failure()
- void callCallback(bool success) {
- if (!callback_called_) {
- callback_called_ = true;
-
- // There are two types of messages we could store in the
- // cache;
- // 1. answers to our fetches from authoritative servers,
- // exactly as we receive them, and
- // 2. answers to queries we received from clients, which
- // have received additional processing (following CNAME
- // chains, for instance)
- //
- // Doing only the first would mean we would have to re-do
- // processing when we get data from our cache, and doing
- // only the second would miss out on the side-effect of
- // having nameserver data in our cache.
- //
- // So right now we do both. Since the cache (currently)
- // stores Messages on their question section only, this
- // does mean that we overwrite the messages we stored in
- // the previous iteration if we are following a delegation.
- if (success) {
- resolvercallback_->success(answer_message_);
- } else {
- resolvercallback_->failure();
- }
- }
- }
-
- // We are done. If there are no more outstanding events, we delete
- // ourselves. If there are any, we do not.
- void stop() {
- done_ = true;
- if (nsas_callback_out_) {
- nsas_.cancel(cur_zone_, question_.getClass(), nsas_callback_);
- nsas_callback_out_ = false;
- }
- client_timer.cancel();
- lookup_timer.cancel();
- if (outstanding_events_ > 0) {
- return;
- } else {
- delete this;
- }
- }
-
- // This function is used as callback from DNSQuery.
- virtual void operator()(IOFetch::Result result) {
- // XXX is this the place for TCP retry?
- assert(outstanding_events_ > 0);
- --outstanding_events_;
-
- if (!done_ && result != IOFetch::TIME_OUT) {
- // we got an answer
-
- // Update the NSAS with the time it took
- struct timeval cur_time;
- gettimeofday(&cur_time, NULL);
- uint32_t rtt = 0;
-
- // Only calculate RTT if it is positive
- if (cur_time.tv_sec > current_ns_qsent_time.tv_sec ||
- (cur_time.tv_sec == current_ns_qsent_time.tv_sec &&
- cur_time.tv_usec > current_ns_qsent_time.tv_usec)) {
- rtt = 1000 * (cur_time.tv_sec - current_ns_qsent_time.tv_sec);
- rtt += (cur_time.tv_usec - current_ns_qsent_time.tv_usec) / 1000;
- }
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_RTT).arg(rtt);
- current_ns_address.updateRTT(rtt);
- if (rtt_recorder_) {
- rtt_recorder_->addRtt(rtt);
- }
-
- try {
- Message incoming(Message::PARSE);
- InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
-
- incoming.fromWire(ibuf);
-
- buffer_->clear();
- done_ = handleRecursiveAnswer(incoming);
- if (done_) {
- callCallback(true);
- stop();
- }
- } catch (const isc::dns::DNSProtocolError& dpe) {
- // Right now, we treat this similar to timeouts
- // (except we don't store RTT)
- // We probably want to make this an integral part
- // of the fetch data process. (TODO)
- if (retries_--) {
- // Retry
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
- RESLIB_PROTOCOL_RETRY)
- .arg(questionText(question_)).arg(dpe.what())
- .arg(retries_);
- send();
- } else {
- // Give up
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
- RESLIB_PROTOCOL)
- .arg(questionText(question_)).arg(dpe.what());
- if (!callback_called_) {
- makeSERVFAIL();
- callCallback(true);
- }
- stop();
- }
- }
- } else if (!done_ && retries_--) {
- // Query timed out, but we have some retries, so send again
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TIMEOUT_RETRY)
- .arg(questionText(question_))
- .arg(current_ns_address.getAddress().toText()).arg(retries_);
- current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
- send();
- } else {
- // We are either already done, or out of retries
- if (result == IOFetch::TIME_OUT) {
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TIMEOUT)
- .arg(questionText(question_))
- .arg(current_ns_address.getAddress().toText());
- current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
- }
- if (!callback_called_) {
- makeSERVFAIL();
- callCallback(true);
- }
- stop();
- }
- }
-
- // Clear the answer parts of answer_message, and set the rcode
- // to servfail
- void makeSERVFAIL() {
- if (answer_message_) {
- isc::resolve::makeErrorMessage(answer_message_, Rcode::SERVFAIL());
- }
- }
-};
-
-class ForwardQuery : public IOFetch::Callback, public AbstractRunningQuery {
-private:
- // The io service to handle async calls
- IOService& io_;
-
- // This is the query message got from client
- ConstMessagePtr query_message_;
-
- // This is where we build and store our final answer
- MessagePtr answer_message_;
-
- // List of nameservers to forward to
- boost::shared_ptr<AddressVector> upstream_;
-
- // Buffer to store the result.
- OutputBufferPtr buffer_;
-
- // This will be notified when we succeed or fail
- isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
-
- /*
- * TODO Do something more clever with timeouts. In the long term, some
- * computation of average RTT, increase with each retry, etc.
- */
- // Timeout information
- int query_timeout_;
-
- // TODO: replace by our wrapper
- asio::deadline_timer client_timer;
- asio::deadline_timer lookup_timer;
-
- // Make ForwardQuery deletes itself safely. for more information see
- // the comments of outstanding_events in RunningQuery.
- size_t outstanding_events_;
-
- // 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_;
-
- // send the query to the server.
- void send(IOFetch::Protocol protocol = IOFetch::UDP) {
- const int uc = upstream_->size();
- buffer_->clear();
- int serverIndex = rand() % uc;
- ConstQuestionPtr question = *(query_message_->beginQuestion());
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_UPSTREAM)
- .arg(questionText(*question))
- .arg(upstream_->at(serverIndex).first);
-
- ++outstanding_events_;
- // Forward the query, create the IOFetch with
- // query message, so that query flags can be forwarded
- // together.
- IOFetch query(protocol, io_, query_message_,
- upstream_->at(serverIndex).first,
- upstream_->at(serverIndex).second,
- buffer_, this, query_timeout_);
-
- io_.get_io_service().post(query);
- }
-
-public:
- ForwardQuery(IOService& io,
- ConstMessagePtr query_message,
- MessagePtr answer_message,
- boost::shared_ptr<AddressVector> upstream,
- OutputBufferPtr buffer,
- isc::resolve::ResolverInterface::CallbackPtr cb,
- int query_timeout, int client_timeout, int lookup_timeout) :
- io_(io),
- query_message_(query_message),
- answer_message_(answer_message),
- upstream_(upstream),
- buffer_(buffer),
- resolvercallback_(cb),
- query_timeout_(query_timeout),
- client_timer(io.get_io_service()),
- lookup_timer(io.get_io_service()),
- outstanding_events_(0),
- callback_called_(false)
- {
- // Setup the timer to stop trying (lookup_timeout)
- if (lookup_timeout >= 0) {
- lookup_timer.expires_from_now(
- boost::posix_time::milliseconds(lookup_timeout));
- ++outstanding_events_;
- lookup_timer.async_wait(boost::bind(&ForwardQuery::lookupTimeout, this));
- }
-
- // Setup the timer to send an answer (client_timeout)
- if (client_timeout >= 0) {
- client_timer.expires_from_now(
- boost::posix_time::milliseconds(client_timeout));
- ++outstanding_events_;
- client_timer.async_wait(boost::bind(&ForwardQuery::clientTimeout, this));
- }
-
- send();
- }
-
- virtual ~ForwardQuery() {};
-
- virtual void lookupTimeout() {
- if (!callback_called_) {
- makeSERVFAIL();
- callCallback(false);
- }
- assert(outstanding_events_ > 0);
- --outstanding_events_;
- stop();
- }
-
- virtual void clientTimeout() {
- if (!callback_called_) {
- makeSERVFAIL();
- callCallback(false);
- }
- assert(outstanding_events_ > 0);
- --outstanding_events_;
- stop();
- }
-
- // If the callback has not been called yet, call it now
- // If success is true, we call 'success' with our answer_message
- // If it is false, we call failure()
- void callCallback(bool success) {
- if (!callback_called_) {
- callback_called_ = true;
- if (success) {
- resolvercallback_->success(answer_message_);
- } else {
- resolvercallback_->failure();
- }
- }
- }
-
- virtual void stop() {
- // if we cancel our timers, we will still get an event for
- // that, so we cannot delete ourselves just yet (those events
- // would be bound to a deleted object)
- // cancel them one by one, both cancels should get us back
- // here again.
- // same goes if we have an outstanding query (can't delete
- // until that one comes back to us)
- lookup_timer.cancel();
- client_timer.cancel();
- if (outstanding_events_ > 0) {
- return;
- } else {
- delete this;
- }
- }
-
- // This function is used as callback from DNSQuery.
- virtual void operator()(IOFetch::Result result) {
- // XXX is this the place for TCP retry?
- assert(outstanding_events_ > 0);
- --outstanding_events_;
- if (result != IOFetch::TIME_OUT) {
- // we got an answer
- Message incoming(Message::PARSE);
- InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
- incoming.fromWire(ibuf);
- isc::resolve::copyResponseMessage(incoming, answer_message_);
- callCallback(true);
- }
-
- stop();
- }
-
- // Clear the answer parts of answer_message, and set the rcode
- // to servfail
- void makeSERVFAIL() {
- isc::resolve::makeErrorMessage(answer_message_, Rcode::SERVFAIL());
- }
-};
-
-}
-
-AbstractRunningQuery*
-RecursiveQuery::resolve(const QuestionPtr& question,
- const isc::resolve::ResolverInterface::CallbackPtr callback)
-{
- IOService& io = dns_service_.getIOService();
-
- MessagePtr answer_message(new Message(Message::RENDER));
- isc::resolve::initResponseMessage(*question, *answer_message);
-
- OutputBufferPtr buffer(new OutputBuffer(0));
-
- // First try to see if we have something cached in the messagecache
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESOLVE)
- .arg(questionText(*question)).arg(1);
- if (cache_.lookup(question->getName(), question->getType(),
- question->getClass(), *answer_message) &&
- answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
- // Message found, return that
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RECQ_CACHE_FIND)
- .arg(questionText(*question)).arg(1);
-
- // TODO: err, should cache set rcode as well?
- answer_message->setRcode(Rcode::NOERROR());
- callback->success(answer_message);
- } else {
- // Perhaps we only have the one RRset?
- // TODO: can we do this? should we check for specific types only?
- RRsetPtr cached_rrset = cache_.lookup(question->getName(),
- question->getType(),
- question->getClass());
- if (cached_rrset) {
- // Found single RRset in cache
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RRSET_FOUND)
- .arg(questionText(*question)).arg(1);
- answer_message->addRRset(Message::SECTION_ANSWER,
- cached_rrset);
- answer_message->setRcode(Rcode::NOERROR());
- callback->success(answer_message);
- } else {
- // Message not found in cache, start recursive query. It will
- // delete itself when it is done
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RECQ_CACHE_NO_FIND)
- .arg(questionText(*question)).arg(1);
- return (new RunningQuery(io, *question, answer_message,
- test_server_, buffer, callback,
- query_timeout_, client_timeout_,
- lookup_timeout_, retries_, nsas_,
- cache_, rtt_recorder_));
- }
- }
- return (NULL);
-}
-
-AbstractRunningQuery*
-RecursiveQuery::resolve(const Question& question,
- MessagePtr answer_message,
- OutputBufferPtr buffer,
- DNSServer* server)
-{
- // XXX: eventually we will need to be able to determine whether
- // the message should be sent via TCP or UDP, or sent initially via
- // UDP and then fall back to TCP on failure, but for the moment
- // we're only going to handle UDP.
- IOService& io = dns_service_.getIOService();
-
- isc::resolve::ResolverInterface::CallbackPtr crs(
- new isc::resolve::ResolverCallbackServer(server));
-
- // TODO: general 'prepareinitialanswer'
- answer_message->setOpcode(isc::dns::Opcode::QUERY());
- answer_message->addQuestion(question);
-
- // First try to see if we have something cached in the messagecache
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESOLVE)
- .arg(questionText(question)).arg(2);
-
- if (cache_.lookup(question.getName(), question.getType(),
- question.getClass(), *answer_message) &&
- answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
-
- // Message found, return that
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RECQ_CACHE_FIND)
- .arg(questionText(question)).arg(2);
- // TODO: err, should cache set rcode as well?
- answer_message->setRcode(Rcode::NOERROR());
- crs->success(answer_message);
- } else {
- // Perhaps we only have the one RRset?
- // TODO: can we do this? should we check for specific types only?
- RRsetPtr cached_rrset = cache_.lookup(question.getName(),
- question.getType(),
- question.getClass());
- if (cached_rrset) {
- // Found single RRset in cache
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RRSET_FOUND)
- .arg(questionText(question)).arg(2);
- answer_message->addRRset(Message::SECTION_ANSWER,
- cached_rrset);
- answer_message->setRcode(Rcode::NOERROR());
- crs->success(answer_message);
-
- } else {
- // Message not found in cache, start recursive query. It will
- // delete itself when it is done
- LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RECQ_CACHE_NO_FIND)
- .arg(questionText(question)).arg(2);
- return (new RunningQuery(io, question, answer_message,
- test_server_, buffer, crs, query_timeout_,
- client_timeout_, lookup_timeout_, retries_,
- nsas_, cache_, rtt_recorder_));
- }
- }
- return (NULL);
-}
-
-AbstractRunningQuery*
-RecursiveQuery::forward(ConstMessagePtr query_message,
- MessagePtr answer_message,
- OutputBufferPtr buffer,
- DNSServer* server,
- isc::resolve::ResolverInterface::CallbackPtr callback)
-{
- // XXX: eventually we will need to be able to determine whether
- // the message should be sent via TCP or UDP, or sent initially via
- // UDP and then fall back to TCP on failure, but for the moment
- // we're only going to handle UDP.
- IOService& io = dns_service_.getIOService();
-
- if (!callback) {
- callback.reset(new isc::resolve::ResolverCallbackServer(server));
- }
-
- // TODO: general 'prepareinitialanswer'
- answer_message->setOpcode(isc::dns::Opcode::QUERY());
- ConstQuestionPtr question = *query_message->beginQuestion();
- answer_message->addQuestion(*question);
-
- // implement the simplest forwarder, which will pass
- // everything throught without interpretation, except
- // QID, port number. The response will not be cached.
- // It will delete itself when it is done
- return (new ForwardQuery(io, query_message, answer_message,
- upstream_, buffer, callback, query_timeout_,
- client_timeout_, lookup_timeout_));
-}
-
-} // namespace asiodns
-} // namespace isc
+++ /dev/null
-// 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.
-
-#ifndef RECURSIVE_QUERY_H
-#define RECURSIVE_QUERY_H 1
-
-#include <util/buffer.h>
-#include <asiodns/dns_service.h>
-#include <asiodns/dns_server.h>
-#include <nsas/nameserver_address_store.h>
-#include <cache/resolver_cache.h>
-
-namespace isc {
-namespace asiodns {
-
-/// \brief RTT Recorder
-///
-/// Used for testing, this class will hold the set of round-trip times to
-/// nameservers for the current recursive query.
-///
-/// A pointer to an object of this class is passed to RecursiveQuery which in
-/// turn passes it to the created RunningQuery class. When a running query
-/// completes, its RTT is passed to the RTT Recorder object.
-class RttRecorder {
-public:
- /// \brief Record Time
- ///
- /// Adds a round-trip time to the internal vector of times.
- ///
- /// \param rtt RTT to record.
- void addRtt(uint32_t rtt) {
- rtt_.push_back(rtt);
- }
-
- /// \brief Return RTT Vector
- std::vector<uint32_t> getRtt() const {
- return rtt_;
- }
-
-private:
- std::vector<uint32_t> rtt_; ///< Stored round-trip times
-};
-
-typedef std::vector<std::pair<std::string, uint16_t> > AddressVector;
-
-/// \brief A Running query
-///
-/// This base class represents an active running query object;
-/// i.e. an outstanding query to an authoritative name server or
-/// upstream server (when running in forwarder mode).
-///
-/// It can not be instantiated directly, but is created by
-/// RecursiveQuery::resolve() and RecursiveQuery::forward().
-///
-/// Its only public method is its destructor, and that should in theory
-/// not be called either except in some unit tests. Instances should
-/// delete themselves when the query is finished.
-class AbstractRunningQuery {
-protected:
- AbstractRunningQuery() {};
-public:
- virtual ~AbstractRunningQuery() {};
-};
-
-/// \brief Recursive Query
-///
-/// The \c RecursiveQuery class provides a layer of abstraction around
-/// the ASIO code that carries out an upstream query.
-
-class RecursiveQuery {
- ///
- /// \name Constructors
- ///
- //@{
-public:
- /// \brief Constructor
- ///
- /// This is currently the only way to construct \c RecursiveQuery
- /// object. If the addresses of the forward nameservers is specified,
- /// and every upstream query will be sent to one random address, and
- /// the result sent back directly. If not, it will do full resolving.
- ///
- /// \param dns_service The DNS Service to perform the recursive
- /// query on.
- /// \param nsas Nameserver address store, used to hold information about zone
- /// nameservers.
- /// \param cache Resolver cache object, used to hold information about retrieved
- /// records.
- /// \param upstream Addresses and ports of the upstream servers
- /// to forward queries to.
- /// \param upstream_root Addresses and ports of the root servers
- /// to use when resolving.
- /// \param query_timeout Timeout value for queries we sent, in ms
- /// \param client_timeout Timeout value for when we send back an
- /// error, in ms
- /// \param lookup_timeout Timeout value for when we give up, in ms
- /// \param retries how many times we try again (0 means just send and
- /// and return if it returs).
- 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 = 2000,
- int client_timeout = 4000,
- int lookup_timeout = 30000,
- unsigned retries = 3);
- //@}
-
- /// \brief Set Round-Trip Time Recorder
- ///
- /// Sets the RTT recorder object. This is not accessed directly, instead
- /// it is passed to created RunningQuery objects.
- ///
- /// \param recorder Pointer to the RTT recorder object used to hold RTTs.
- void setRttRecorder(boost::shared_ptr<RttRecorder>& recorder);
-
- /// \brief Initiate resolving
- ///
- /// When sendQuery() is called, a (set of) message(s) is sent
- /// asynchronously. If upstream servers are set, one is chosen
- /// and the response (if any) from that server will be returned.
- ///
- /// If not upstream is set, a root server is chosen from the
- /// root_servers, and the RunningQuery shall do a full resolve
- /// (i.e. if the answer is a delegation, it will be followed, etc.)
- /// until there is an answer or an error.
- ///
- /// When there is a response or an error and we give up, the given
- /// CallbackPtr object shall be called (with either success() or
- /// failure(). See ResolverInterface::Callback for more information.
- ///
- /// \param question The question being answered <qname/qclass/qtype>
- /// \param callback Callback object. See
- /// \c ResolverInterface::Callback for more information
- AbstractRunningQuery* resolve(const isc::dns::QuestionPtr& question,
- const isc::resolve::ResolverInterface::CallbackPtr callback);
-
-
- /// \brief Initiates resolving for the given question.
- ///
- /// This actually calls the previous sendQuery() with a default
- /// callback object, which calls resume() on the given DNSServer
- /// object.
- ///
- /// \param question The question being answered <qname/qclass/qtype>
- /// \param answer_message An output Message into which the final response will
- /// be copied.
- /// \param buffer An output buffer into which the intermediate responses will
- /// be copied.
- /// \param server A pointer to the \c DNSServer object handling the client
- /// \return A pointer to the active AbstractRunningQuery object
- /// created by this call (if any); this object should delete
- /// itself in normal circumstances, and can normally be ignored
- /// by the caller, but a pointer is returned for use-cases
- /// such as unit tests.
- /// Returns NULL if the data was found internally and no actual
- /// query was sent.
- AbstractRunningQuery* resolve(const isc::dns::Question& question,
- isc::dns::MessagePtr answer_message,
- isc::util::OutputBufferPtr buffer,
- DNSServer* server);
-
- /// \brief Initiates forwarding for the given query.
- ///
- /// Others parameters are same with the parameters of
- /// function resolve().
- ///
- /// \param query_message the full query got from client.
- /// \param answer_message the full answer received from other server.
- /// \param buffer Output buffer into which the responses will be copied.
- /// \param server Server object that handles receipt and processing of the
- /// received messages.
- /// \param callback callback object
- /// \return A pointer to the active ForwardQuery created by this call;
- /// this object should delete itself in normal circumstances,
- /// and can normally be ignored by the caller, but a pointer
- /// is returned for use-cases such as unit tests.
- AbstractRunningQuery* forward(isc::dns::ConstMessagePtr query_message,
- isc::dns::MessagePtr answer_message,
- isc::util::OutputBufferPtr buffer,
- DNSServer* server,
- isc::resolve::ResolverInterface::CallbackPtr callback =
- isc::resolve::ResolverInterface::CallbackPtr());
-
- /// \brief Set Test Server
- ///
- /// This method is *only* for unit testing the class. If set, it enables
- /// recursive behaviour but, regardless of responses received, sends every
- /// query to the test server.
- ///
- /// The test server is enabled by setting a non-zero port number.
- ///
- /// \param address IP address of the test server.
- /// \param port Port number of the test server
- void setTestServer(const std::string& address, uint16_t port);
-
-private:
- DNSServiceBase& dns_service_;
- isc::nsas::NameserverAddressStore& nsas_;
- isc::cache::ResolverCache& cache_;
- boost::shared_ptr<std::vector<std::pair<std::string, uint16_t> > >
- upstream_;
- boost::shared_ptr<std::vector<std::pair<std::string, uint16_t> > >
- upstream_root_;
- std::pair<std::string, uint16_t> test_server_;
- int query_timeout_;
- int client_timeout_;
- int lookup_timeout_;
- unsigned retries_;
- boost::shared_ptr<RttRecorder> rtt_recorder_; ///< Round-trip time recorder
-};
-
-} // namespace asiodns
-} // namespace isc
-#endif // RECURSIVE_QUERY_H
+++ /dev/null
-// 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 <resolve/resolve.h>
-
-#include <dns/message.h>
-#include <dns/opcode.h>
-
-using namespace isc::dns;
-
-namespace {
- class SectionInserter {
- public:
- SectionInserter(MessagePtr message, const Message::Section sect) :
- message_(message), section_(sect)
- {}
- void operator()(const RRsetPtr rrset) {
- message_->addRRset(section_, rrset);
- }
- MessagePtr message_;
- const Message::Section section_;
- };
-}
-
-namespace isc {
-namespace resolve {
-
-void
-makeErrorMessage(MessagePtr answer_message,
- const Rcode& error_code)
-{
- answer_message->clearSection(Message::SECTION_ANSWER);
- answer_message->clearSection(Message::SECTION_AUTHORITY);
- answer_message->clearSection(Message::SECTION_ADDITIONAL);
-
- answer_message->setRcode(error_code);
-}
-
-void initResponseMessage(const isc::dns::Message& query_message,
- isc::dns::Message& response_message)
-{
- response_message.setOpcode(query_message.getOpcode());
- response_message.setQid(query_message.getQid());
- assert(response_message.getRRCount(Message::SECTION_QUESTION) == 0);
- response_message.appendSection(Message::SECTION_QUESTION,
- query_message);
-}
-
-void initResponseMessage(const isc::dns::Question& question,
- isc::dns::Message& response_message)
-{
- response_message.setOpcode(isc::dns::Opcode::QUERY());
- response_message.addQuestion(question);
-}
-
-void copyResponseMessage(const Message& source, MessagePtr target) {
- target->setRcode(source.getRcode());
-
- target->appendSection(Message::SECTION_ANSWER, source);
- target->appendSection(Message::SECTION_AUTHORITY, source);
- target->appendSection(Message::SECTION_ADDITIONAL, source);
-}
-
-
-} // namespace resolve
-} // namespace isc
-
+++ /dev/null
-// 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.
-
-#ifndef ISC_RESOLVE_H
-#define ISC_RESOLVE_H 1
-
-/// This file includes all other libresolve headers, and provides
-/// several helper functions used in resolving.
-
-#include <resolve/resolver_interface.h>
-#include <resolve/resolver_callback.h>
-#include <resolve/response_classifier.h>
-
-#include <dns/rcode.h>
-
-namespace isc {
-namespace resolve {
-
-/// \brief Create an error response
-///
-/// Clears the answer, authority, and additional section of the
-/// given MessagePtr and sets the given error code
-///
-/// Notes: Assuming you have already done initial preparations
-/// on the given answer message (copy the opcode, qid and question
-/// section), you can simply use this to create an error response.
-///
-/// \param answer_message The message to clear and place the error in
-/// \param error_code The error Rcode
-void makeErrorMessage(isc::dns::MessagePtr answer_message,
- const isc::dns::Rcode& error_code);
-
-
-/// \brief Initialize a response message
-///
-/// Based on the given query message, this fills in the very
-/// first details of the response (i.e. the Question section and
-/// the Opcode). This allows for direct usage of makeErrorMessage(),
-/// as well as ResolveCache.lookup().
-///
-/// Raises an isc::dns::InvalidMessageOperation if reponse_message is
-/// not in RENDER mode.
-///
-/// \param query_message The query message to take the Question, Qid,
-/// and Opcode from.
-/// \param response_message The fresh response message to initialize
-/// (must be in RENDER mode)
-void initResponseMessage(const isc::dns::Message& query_message,
- isc::dns::Message& response_message);
-
-
-/// \brief Initialize a response message
-///
-/// Based on the given question, this fills in the very
-/// first details of the response (i.e. the Question section and the
-/// Opcode Query). This allows for direct usage of makeErrorMessage(),
-/// as well as ResolveCache.lookup().
-///
-/// Raises an isc::dns::InvalidMessageOperation if reponse_message is
-/// not in RENDER mode.
-///
-/// \param question The question to place in the Question section
-/// \param response_message The fresh response message to initialize
-/// (must be in RENDER mode)
-void initResponseMessage(const isc::dns::Question& question,
- isc::dns::Message& response_message);
-
-
-/// \brief Copies the parts relevant for a DNS response to the
-/// target message
-///
-/// This adds all the RRsets in the answer, authority and
-/// additional sections to the target, as well as the response
-/// code
-/// \param source The Message to copy the data from
-/// \param target The Message to copy the data to
-void copyResponseMessage(const isc::dns::Message& source,
- isc::dns::MessagePtr target);
-
-
-} // namespace resolve
-} // namespace isc
-
-#endif // ISC_RESOLVE_H_
+++ /dev/null
-// 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.
-
-/// Defines the logger used by the NSAS
-
-#include <resolve/resolve_log.h>
-
-namespace isc {
-namespace resolve {
-
-isc::log::Logger logger("reslib"); // Distinct from "resolver"
-
-} // namespace resolve
-} // namespace isc
-
+++ /dev/null
-// 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.
-
-#ifndef RESOLVE_LOG_H
-#define RESOLVE_LOG_H
-
-#include <log/macros.h>
-#include "resolve_messages.h"
-
-namespace isc {
-namespace resolve {
-
-/// \brief Resolver Library Logging
-///
-/// Defines the levels used to output debug messages in the resolver library.
-/// Note that higher numbers equate to more verbose (and detailed) output.
-
-// The first level traces normal operations
-const int RESLIB_DBG_TRACE = DBGLVL_TRACE_BASIC;
-
-// The next level extends the normal operations and records the results of the
-// lookups.
-const int RESLIB_DBG_RESULTS = DBGLVL_TRACE_BASIC_DATA;
-
-// Report cache lookups and results
-const int RESLIB_DBG_CACHE = DBGLVL_TRACE_DETAIL_DATA;
-
-// Indicate when callbacks are called
-const int RESLIB_DBG_CB = DBGLVL_TRACE_DETAIL_DATA + 10;
-
-
-/// \brief Resolver Library Logger
-///
-/// Define the logger used to log messages. We could define it in multiple
-/// modules, but defining in a single module and linking to it saves time and
-/// space.
-extern isc::log::Logger logger;
-
-} // namespace resolve
-} // namespace isc
-
-#endif // RESOLVE_LOG_H
+++ /dev/null
-# 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.
-
-$NAMESPACE isc::resolve
-
-% RESLIB_ANSWER answer received in response to query for <%1>
-A debug message reporting that an answer has been received to an upstream
-query for the specified question. Previous debug messages will have
-indicated the server to which the question was sent.
-
-% RESLIB_CNAME CNAME received in response to query for <%1>
-A debug message recording that CNAME response has been received to an
-upstream query for the specified question. Previous debug messages will
-have indicated the server to which the question was sent.
-
-% RESLIB_DEEPEST did not find <%1> in cache, deepest delegation found is %2
-A debug message, a cache lookup did not find the specified <name,
-class, type> tuple in the cache; instead, the deepest delegation found
-is indicated.
-
-% RESLIB_EMPTY_RESPONSE empty response received to query for <%1>
-A debug message, the response to the specified query from an upstream
-nameserver did not contain anything in the answer or authority sections,
-although in all other respects it was a valid response. A SERVFAIL will
-be returned to the system making the original query.
-
-% RESLIB_ERROR_RESPONSE unspecified error received in response to query for <%1>
-A debug message, the response to the specified query to an upstream
-nameserver indicated that the response was classified as an erroneous
-response, but that the nature of the error cannot be identified.
-A SERVFAIL will be returned to the system making the original query.
-
-% RESLIB_EXTRADATA_RESPONSE extra data in response to query for <%1>
-A debug message indicating that the response to the specified query
-from an upstream nameserver contained too much data. This can happen if
-an ANY query was sent and the answer section in the response contained
-multiple RRs with different names. A SERVFAIL will be returned to the
-system making the original query.
-
-% RESLIB_FOLLOW_CNAME following CNAME chain to <%1>
-A debug message, a CNAME response was received and another query is
-being issued for the <name, class, type> tuple.
-
-% RESLIB_INVALID_NAMECLASS_RESPONSE invalid name or class in response to query for <%1>
-A debug message, the response to the specified query from an upstream
-nameserver (as identified by the ID of the response) contained either
-an answer not matching the query name or an answer having a different
-class to that queried for. A SERVFAIL will be returned to the system
-making the original query.
-
-% RESLIB_INVALID_QNAME_RESPONSE invalid name or class in response to query for <%1>
-A debug message, the response to the specified query from an upstream
-nameserver (as identified by the ID of the response) contained a name
-in the question section that did not match that of the query. A SERVFAIL
-will be returned to the system making the original query.
-
-% RESLIB_INVALID_TYPE_RESPONSE invalid name or class in response to query for <%1>
-A debug message, the response to the specified query from an upstream
-nameserver (as identified by the ID of the response) contained an
-invalid type field. A SERVFAIL will be returned to the system making
-the original query.
-
-% RESLIB_LONG_CHAIN CNAME received in response to query for <%1>: CNAME chain length exceeded
-A debug message recording that a CNAME response has been received to an upstream
-query for the specified question (Previous debug messages will have indicated
-the server to which the question was sent). However, receipt of this CNAME
-has meant that the resolver has exceeded the CNAME chain limit (a CNAME chain
-is where on CNAME points to another) and so an error is being returned.
-
-% RESLIB_MULTIPLE_CLASS_RESPONSE response to query for <%1> contained multiple RRsets with different classes
-A debug message reporting that the response to an upstream query for
-the specified name contained multiple RRsets in the answer and not all
-were of the same class. This is a violation of the standard and so a
-SERVFAIL will be returned.
-
-% RESLIB_NOTSINGLE_RESPONSE response to query for <%1> was not a response
-A debug message, the response to the specified query from an upstream
-nameserver was a CNAME that had multiple RRs in the RRset. This is
-an invalid response according to the standards so a SERVFAIL will be
-returned to the system making the original query.
-
-% RESLIB_NOT_ONE_QNAME_RESPONSE not one question in response to query for <%1>
-A debug message, the response to the specified query from an upstream
-nameserver (as identified by the ID of the response) did not contain
-one name in the question section as required by the standard. A SERVFAIL
-will be returned to the system making the original query.
-
-% RESLIB_NOT_RESPONSE response to query for <%1> was not a response
-A debug message, the response to the specified query from an upstream
-nameserver (as identified by the ID of the response) did not have the QR
-bit set (thus indicating that the packet was a query, not a response).
-A SERVFAIL will be returned to the system making the original query.
-
-% RESLIB_NO_NS_RRSET no NS RRSet in referral response received to query for <%1>
-A debug message, this indicates that a response was received for the specified
-query and was categorized as a referral. However, the received message did
-not contain any NS RRsets. This may indicate a programming error in the
-response classification code.
-
-% RESLIB_NSAS_LOOKUP looking up nameserver for zone %1 in the NSAS
-A debug message, the RunningQuery object is querying the NSAS for the
-nameservers for the specified zone.
-
-% RESLIB_NXDOM_NXRR NXDOMAIN/NXRRSET received in response to query for <%1>
-A debug message recording that either a NXDOMAIN or an NXRRSET response has
-been received to an upstream query for the specified question. Previous debug
-messages will have indicated the server to which the question was sent.
-
-% RESLIB_OPCODE_RESPONSE response to query for <%1> did not have query opcode
-A debug message, the response to the specified query from an upstream
-nameserver was a response that did not have the opcode set to that of
-a query. According to the standards, this is an invalid response to
-the query that was made, so a SERVFAIL will be returned to the system
-making the original query.
-
-% RESLIB_PROTOCOL protocol error in answer for %1: %3
-A debug message indicating that a protocol error was received. As there
-are no retries left, an error will be reported.
-
-% RESLIB_PROTOCOL_RETRY protocol error in answer for %1: %2 (retries left: %3)
-A debug message indicating that a protocol error was received and that
-the resolver is repeating the query to the same nameserver. After this
-repeated query, there will be the indicated number of retries left.
-
-% RESLIB_RCODE_RETURNED response to query for <%1> returns RCODE of %2
-A debug message, the response to the specified query indicated an error
-that is not covered by a specific code path. A SERVFAIL will be returned.
-
-% RESLIB_RECQ_CACHE_FIND found <%1> in the cache (resolve() instance %2)
-This is a debug message and indicates that a RecursiveQuery object found
-the specified <name, class, type> tuple in the cache. The instance number
-at the end of the message indicates which of the two resolve() methods has
-been called.
-
-% RESLIB_RECQ_CACHE_NO_FIND did not find <%1> in the cache, starting RunningQuery (resolve() instance %2)
-This is a debug message and indicates that the look in the cache made by the
-RecursiveQuery::resolve() method did not find an answer, so a new RunningQuery
-object has been created to resolve the question. The instance number at
-the end of the message indicates which of the two resolve() methods has
-been called.
-
-% RESLIB_REFERRAL referral received in response to query for <%1>
-A debug message recording that a referral response has been received to an
-upstream query for the specified question. Previous debug messages will
-have indicated the server to which the question was sent.
-
-% RESLIB_REFER_ZONE referred to zone %1
-A debug message indicating that the last referral message was to the specified
-zone.
-
-% RESLIB_RESOLVE asked to resolve <%1> (resolve() instance %2)
-A debug message, the RecursiveQuery::resolve method has been called to resolve
-the specified <name, class, type> tuple. The first action will be to lookup
-the specified tuple in the cache. The instance number at the end of the
-message indicates which of the two resolve() methods has been called.
-
-% RESLIB_RRSET_FOUND found single RRset in the cache when querying for <%1> (resolve() instance %2)
-A debug message, indicating that when RecursiveQuery::resolve queried the
-cache, a single RRset was found which was put in the answer. The instance
-number at the end of the message indicates which of the two resolve()
-methods has been called.
-
-% RESLIB_RTT round-trip time of last query calculated as %1 ms
-A debug message giving the round-trip time of the last query and response.
-
-% RESLIB_RUNQ_CACHE_FIND found <%1> in the cache
-This is a debug message and indicates that a RunningQuery object found
-the specified <name, class, type> tuple in the cache.
-
-% RESLIB_RUNQ_CACHE_LOOKUP looking up <%1> in the cache
-This is a debug message and indicates that a RunningQuery object has made
-a call to its doLookup() method to look up the specified <name, class, type>
-tuple, the first action of which will be to examine the cache.
-
-% RESLIB_RUNQ_FAIL failure callback - nameservers are unreachable
-A debug message indicating that a RunningQuery's failure callback has been
-called because all nameservers for the zone in question are unreachable.
-
-% RESLIB_RUNQ_SUCCESS success callback - sending query to %1
-A debug message indicating that a RunningQuery's success callback has been
-called because a nameserver has been found, and that a query is being sent
-to the specified nameserver.
-
-% RESLIB_TCP_TRUNCATED TCP response to query for %1 was truncated
-This is a debug message logged when a response to the specified query to an
-upstream nameserver returned a response with the TC (truncation) bit set. This
-is treated as an error by the code.
-
-% RESLIB_TEST_SERVER setting test server to %1(%2)
-This is a warning message only generated in unit tests. It indicates
-that all upstream queries from the resolver are being routed to the
-specified server, regardless of the address of the nameserver to which
-the query would normally be routed. If seen during normal operation,
-please submit a bug report.
-
-% RESLIB_TEST_UPSTREAM sending upstream query for <%1> to test server at %2
-This is a debug message and should only be seen in unit tests. A query for
-the specified <name, class, type> tuple is being sent to a test nameserver
-whose address is given in the message.
-
-% RESLIB_TIMEOUT query <%1> to %2 timed out
-A debug message indicating that the specified upstream query has timed out and
-there are no retries left.
-
-% RESLIB_TIMEOUT_RETRY query <%1> to %2 timed out, re-trying (retries left: %3)
-A debug message indicating that the specified query has timed out and that
-the resolver is repeating the query to the same nameserver. After this
-repeated query, there will be the indicated number of retries left.
-
-% RESLIB_TRUNCATED response to query for <%1> was truncated, re-querying over TCP
-A debug message, this indicates that the response to the specified query was
-truncated and that the resolver will be re-querying over TCP. There are
-various reasons why responses may be truncated, so this message is normal and
-gives no cause for concern.
-
-% RESLIB_UPSTREAM sending upstream query for <%1> to %2
-A debug message indicating that a query for the specified <name, class, type>
-tuple is being sent to a nameserver whose address is given in the message.
+++ /dev/null
-// 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 <resolve/resolver_callback.h>
-
-namespace isc {
-namespace resolve {
-
-void
-ResolverCallbackServer::success(const isc::dns::MessagePtr response)
-{
- // ignore our response here
- (void)response;
-
- server_->resume(true);
-}
-
-void
-ResolverCallbackServer::failure()
-{
- server_->resume(false);
-}
-
-} // namespace resolve
-} // namespace isc
+++ /dev/null
-// 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.
-
-#ifndef ISC_RESOLVER_CALLBACK_H
-#define ISC_RESOLVER_CALLBACK_H 1
-
-#include <asiodns/dns_server.h>
-#include <dns/message.h>
-
-#include <resolve/resolver_interface.h>
-
-namespace isc {
-namespace resolve {
-
-/// \short Standard Callback for sendQuery for DNSServer instances
-///
-/// This is a standard ResolverInterface::Callback implementation
-/// that is used by Resolver; when RunningQuery finishes and has either
-/// some data or an error, DNSServer::resume() will be called.
-///
-/// This class will ignore the response MessagePtr in the callback,
-/// as the server itself should also have a reference.
-class ResolverCallbackServer : public ResolverInterface::Callback {
-public:
- ResolverCallbackServer(asiodns::DNSServer* server) :
- server_(server->clone()) {}
- ~ResolverCallbackServer() { delete server_; };
-
- void success(const isc::dns::MessagePtr response);
- void failure();
-
-private:
- asiodns::DNSServer* server_;
-};
-
-} //namespace resolve
-} //namespace isc
-
-#endif // ISC_RESOLVER_CALLBACK_H_
+++ /dev/null
-// Copyright (C) 2010 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.
-
-#ifndef RESOLVER_INTERFACE_H
-#define RESOLVER_INTERFACE_H
-
-#include <dns/message.h>
-
-///
-/// \file resolver_interface.h
-/// \short Interface to resolver.
-///
-/// This file contains an interface for the resolver. By subclassing
-/// this abstract interface, other parts of the system can ask the
-/// resolver to do some resolving too.
-///
-/// This is done by creating a subclass of ResolverInterface::Callback,
-/// which defines what to do with the result, and then calling resolve()
-/// on the ResolverInterface implementation.
-///
-/// One default Callback subclass is provided right now, in
-/// resolver_callback.[h|cc], which calls resumse() on a given DNSServer
-///
-
-namespace isc {
-namespace resolve {
-
-///
-/// \short Abstract interface to the resolver.
-///
-/// Abstract interface to the resolver. The NameserverAddressStore uses this
-/// to ask for addresses. It is here because resolver does not yet exist.
-///
-/// It is abstract to allow tests pass dummy resolvers.
-///
-class ResolverInterface {
- public:
- /// \short An abstract callback for when the resolver is done.
- ///
- /// You can pass an instance of a subclass of this (as a
- /// CallbackPtr) to RecursiveQuery::sendQuery(), and when it
- /// is done, it will either call success() if there is an
- /// answer MessagePtr, or failure(), if the resolver was not
- /// able to find anything.
- ///
- /// Note that a result Message does not necessarily contain
- /// the actual answer (it could be a noerror/nodata response).
- class Callback {
- public:
- /// \short Some data arrived.
- virtual void success(const isc::dns::MessagePtr response) = 0;
-
- ///
- ///\short No data available.
- ///
- ///\todo Provide error reason (result of the
- /// classification call, for instance? We'd also
- /// need some way to say 'everything times out')
- ///
- virtual void failure() = 0;
-
- /// \short Virtual destructor, so descendants are cleaned up
- virtual ~Callback() {};
- };
-
- typedef boost::shared_ptr<Callback> CallbackPtr;
-
- ///
- ///\short Ask a question.
- ///
- /// Asks the resolver a question. Once the answer is ready
- /// the callback is called.
- ///
- /// \param question What to ask. The resolver will decide who.
- /// \param callback What should happen when the answer is ready.
- ///
- virtual void resolve(const isc::dns::QuestionPtr& question,
- const CallbackPtr& callback) = 0;
-
- /// \short Virtual destructor, so descendants are properly cleaned up
- virtual ~ ResolverInterface() {}
-};
-
-} // namespace nsas
-} // namespace isc
-
-#endif //RESOLVER_INTERFACE_H
+++ /dev/null
-// 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.
-
-// $Id$
-
-#include <cstddef>
-#include <vector>
-
-#include <resolve/response_classifier.h>
-#include <dns/name.h>
-#include <dns/opcode.h>
-#include <dns/rcode.h>
-#include <dns/rrset.h>
-
-using namespace isc::dns;
-using namespace std;
-
-namespace isc {
-namespace resolve {
-
-// Classify the response in the "message" object.
-
-ResponseClassifier::Category ResponseClassifier::classify(
- const Question& question, const Message& message,
- Name& cname_target, unsigned int& cname_count, bool tcignore
- )
-{
- // Check header bits
- if (!message.getHeaderFlag(Message::HEADERFLAG_QR)) {
- return (NOTRESPONSE); // Query-response bit not set in the response
- }
-
- // We only recognise responses to queries here
- if (message.getOpcode() != Opcode::QUERY()) {
- return (OPCODE);
- }
-
- // Apparently have a response. There must be a single question in it...
- const vector<QuestionPtr> msgquestion(message.beginQuestion(),
- message.endQuestion());
- if (msgquestion.size() != 1) {
- return (NOTONEQUEST); // Not one question in response question section
- }
-
- // ... and the question should be equal to the question given.
- // XXX: This means that "question" may not be the question sent by the
- // client. In the case of a CNAME response, the qname of subsequent
- // questions needs to be altered.
- if (question != *(msgquestion[0])) {
- return (MISMATQUEST);
- }
-
- // Check for Rcode-related errors.
- const Rcode& rcode = message.getRcode();
- if (rcode != Rcode::NOERROR()) {
- if (rcode == Rcode::NXDOMAIN()) {
-
- // No such domain. According to RFC2308, the domain referred to by
- // the QNAME does not exist, although there may be a CNAME in the
- // answer section and there may be an SOA and/or NS RRs in the
- // authority section (ignoring any DNSSEC RRs for now).
- //
- // Note the "may". There may not be anything. Also, note that if
- // there is a CNAME in the answer section, the authoritative server
- // has verified that the name given in the CNAME's RDATA field does
- // not exist. And that if a CNAME is returned in the answer, then
- // the QNAME of the RRs in the authority section will refer to the
- // authority for the CNAME's RDATA and not to the original question.
- //
- // Without doing further classification, it is sufficient to say
- // that if an NXDOMAIN is received, there was no translation of the
- // QNAME available.
- return (NXDOMAIN); // Received NXDOMAIN from parent.
-
- } else {
-
- // Not NXDOMAIN but not NOERROR either. Must be an RCODE-related
- // error.
- return (RCODE);
- }
- }
-
- // All seems OK and we can start looking at the content. However, one
- // more header check remains - was the response truncated? If so, we'll
- // probably want to re-query over TCP. However, in some circumstances we
- // might want to go with what we have. So give the caller the option of
- // ignoring the TC bit.
- if (message.getHeaderFlag(Message::HEADERFLAG_TC) && (!tcignore)) {
- return (TRUNCATED);
- }
-
- // By the time we get here, we're assured that the packet format is correct.
- // We now need to decide as to whether it is an answer, a CNAME, or a
- // referral. For this, we need to inspect the contents of the answer
- // and authority sections.
- const vector<RRsetPtr> answer(
- message.beginSection(Message::SECTION_ANSWER),
- message.endSection(Message::SECTION_ANSWER)
- );
- const vector<RRsetPtr> authority(
- message.beginSection(Message::SECTION_AUTHORITY),
- message.endSection(Message::SECTION_AUTHORITY)
- );
-
- // If there is nothing in the answer section, it is a referral - unless
- // there is no NS in the authority section
- if (answer.empty()) {
- if (authority.empty()) {
- return (EMPTY);
- }
- for (vector<RRsetPtr>::size_type i = 0; i < authority.size(); ++i) {
- if (authority[i]->getType() == RRType::NS()) {
- return (REFERRAL);
- }
- }
- return (NXRRSET);
- }
-
- // Look at two cases - one RRset in the answer and multiple RRsets in
- // the answer.
- if (answer.size() == 1) {
-
- // Does the name and class of the answer match that of the question?
- if ((answer[0]->getName() == question.getName()) &&
- (answer[0]->getClass() == question.getClass())) {
-
- // It does. How about the type of the response? The response
- // is an answer if the type matches that of the question, or if the
- // question was for type ANY. It is a CNAME reply if the answer
- // type is CNAME. And it is an error for anything else.
- if ((answer[0]->getType() == question.getType()) ||
- (question.getType() == RRType::ANY())) {
- return (ANSWER);
- } else if (answer[0]->getType() == RRType::CNAME()) {
- RdataIteratorPtr it = answer[0]->getRdataIterator();
- cname_target = Name(it->getCurrent().toText());
- ++cname_count;
- return (CNAME);
- } else {
- return (INVTYPE);
- }
- }
- else {
-
- // Either the name and/or class of the reply don't match that of
- // the question.
- return (INVNAMCLASS);
- }
- }
-
- // There are multiple RRsets in the answer. They should all have the same
- // QCLASS, else there is some error in the response.
- for (vector<RRsetPtr>::size_type i = 1; i < answer.size(); ++i) {
- if (answer[0]->getClass() != answer[i]->getClass()) {
- return (MULTICLASS);
- }
- }
-
- // If the request type was ANY and they all have the same QNAME, we have
- // an answer. But if they don't have the same QNAME, we must have an error;
- // the only way we could get different QNAMES in an answer is if one were a
- // CNAME - in which case there should no other record types at that QNAME.
- if (question.getType() == RRType::ANY()) {
- bool all_same = true;
- for (vector<RRsetPtr>::size_type i = 1; (i < answer.size()) && all_same;
- ++i) {
- all_same = (answer[0]->getName() == answer[i]->getName());
- }
- if (all_same) {
- return (ANSWER);
- } else {
- return (EXTRADATA);
- }
- }
-
- // Multiple RRs in the answer, and not all the same QNAME. This
- // is either an answer, a CNAME (in either case, there could be multiple
- // CNAMEs in the chain) or an error.
- //
- // So we need to follow the CNAME chain to resolve this. For this to work:
- //
- // a) There must be one RR that matches the name, class and type of
- // the question, and this is a CNAME.
- // b) The CNAME chain is followed until the end of the chain does not
- // exist (answer is a CNAME) or it is not of type CNAME (ANSWER).
- //
- // In the latter case, if there are additional RRs, it must be an error.
-
- vector<RRsetPtr> ansrrset(answer);
- vector<int> present(ansrrset.size(), 1);
- return cnameChase(question.getName(), question.getType(),
- cname_target, cname_count,
- ansrrset, present, ansrrset.size());
-}
-
-// Search the CNAME chain.
-ResponseClassifier::Category ResponseClassifier::cnameChase(
- const Name& qname, const RRType& qtype,
- Name& cname_target, unsigned int& cname_count,
- vector<RRsetPtr>& ansrrset, vector<int>& present, size_t size)
-{
- // Search through the vector of RRset pointers until we find one with the
- // right QNAME.
- for (vector<RRsetPtr>::size_type i = 0; i < ansrrset.size(); ++i) {
- if (present[i]) {
-
- // This entry has not been logically removed, so look at it.
- if (ansrrset[i]->getName() == qname) {
-
- // QNAME match. If this RRset is a CNAME, remove it from
- // further consideration. If nothing is left, the end of the
- // chain is a CNAME so this is a CNAME. Otherwise replace
- // the name with the RDATA of the CNAME and call ourself
- // recursively.
- if (ansrrset[i]->getType() == RRType::CNAME()) {
-
- // Don't consider it in the next iteration (although we
- // can still access it for now).
- present[i] = 0;
- --size;
- if (size == 0) {
- RdataIteratorPtr it = ansrrset[i]->getRdataIterator();
- cname_target = Name(it->getCurrent().toText());
- return (CNAME);
- } else {
- if (ansrrset[i]->getRdataCount() != 1) {
-
- // Multiple RDATA for a CNAME? This is invalid.
-
- return (NOTSINGLE);
- }
- RdataIteratorPtr it = ansrrset[i]->getRdataIterator();
- Name newname(it->getCurrent().toText());
-
- // Increase CNAME count, and continue
- return cnameChase(newname, qtype, cname_target,
- ++cname_count, ansrrset, present, size);
- }
-
- } else {
-
- // We've got here because the element is not a CNAME. If
- // this is the last element and the type is the one we are
- // after, we've found the answer, or it is an error. If
- // there is more than one RRset left in the list we are
- // searching, we have extra data in the answer.
- if (ansrrset[i]->getType() == qtype) {
- if (size == 1) {
- return (ANSWERCNAME);
- } else {
- return (EXTRADATA);
- }
- }
- return (INVTYPE);
- }
- }
- }
- }
-
- // We get here if we've dropped off the end of the list without finding the
- // QNAME we are looking for. This means that the CNAME chain has ended
- // but there are additional RRsets in the data.
-
- return (EXTRADATA);
-}
-
-} // namespace resolve
-} // namespace isc
+++ /dev/null
-// 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.
-
-// $Id$
-
-#ifndef RESPONSE_CLASSIFIER_H
-#define RESPONSE_CLASSIFIER_H
-
-#include <cstddef>
-
-#include <dns/question.h>
-#include <dns/message.h>
-#include <dns/question.h>
-
-#define RESOLVER_MAX_CNAME_CHAIN 16
-
-namespace isc {
-namespace resolve {
-
-/// \brief Classify Server Response
-///
-/// This class is used in the recursive server. It is passed an answer received
-/// from an upstream server and categorises it.
-///
-/// TODO: The code here does not take into account any EDNS0 fields.
-
-class ResponseClassifier {
-public:
-
- /// \brief Category of Answer
- ///
- /// In the valid answers, not the distinction between REFERRAL and CNAME.
- /// A REFERRAL answer means that the answer section of the message is
- /// empty, but there is something in the authority section. A CNAME means
- /// that the answer section contains one or more CNAMES in a chain that
- /// do not end with a non-CNAME RRset.
- enum Category {
-
- // Codes indicating that a message is valid.
-
- ANSWER, ///< Response contains the answer
- ANSWERCNAME, ///< Response was a CNAME chain ending in an answer
- CNAME, ///< Response was a CNAME
- NXDOMAIN, ///< Response was an NXDOMAIN
- NXRRSET, ///< Response was name exists, but type does not
- REFERRAL, ///< Response contains a referral
-
- // Codes indicating that a message is invalid. Note that the error()
- // method relies on these appearing after the "message valid" codes.
-
- EMPTY, ///< No answer or authority sections
- EXTRADATA, ///< Answer section contains more RRsets than needed
- INVNAMCLASS, ///< Invalid name or class in answer
- INVTYPE, ///< Name/class of answer correct, type is wrong
- MISMATQUEST, ///< Response question section != question
- MULTICLASS, ///< Multiple classes in multi-RR answer
- NOTONEQUEST, ///< Not one question in response question section
- NOTRESPONSE, ///< Response has the Query/Response bit clear
- NOTSINGLE, ///< CNAME has multiple RDATA elements.
- OPCODE, ///< Opcode field does not indicate a query
- RCODE, ///< RCODE indicated an error
- TRUNCATED ///< Response was truncated
- };
-
- /// \brief Check Error
- ///
- /// An inline routine to quickly classify whether the return category is
- /// an error or not. This makes use of internal knowledge of the order of
- /// codes in the Category enum.
- ///
- /// \param code Return category from classify()
- ///
- /// \return true if the category is an error, false if not.
- static bool error(Category code) {
- return (code > REFERRAL);
- }
-
- /// \brief Classify
- ///
- /// Classify the response in the "message" object.
- ///
- /// \param question Question that was sent to the server
- /// \param message Pointer to the associated response from the server.
- /// \param cname_target If the message contains an (unfinished) CNAME
- /// chain, this Name will be replaced by the target of the last CNAME
- /// in the chain
- /// \param cname_count This unsigned int will be incremented with
- /// the number of CNAMEs followed
- /// \param tcignore If set, the TC bit in a response packet is
- /// ignored. Otherwise the error code TRUNCATED will be returned. The
- /// only time this is likely to be used is in development where we are not
- /// going to fail over to TCP and will want to use what is returned, even
- /// if some of the response was lost.
- static Category classify(const isc::dns::Question& question,
- const isc::dns::Message& message,
- isc::dns::Name& cname_target, unsigned int& cname_count,
- bool tcignore = false);
-
-private:
- /// \brief Follow CNAMEs
- ///
- /// Given a QNAME and an answer section that contains CNAMEs, assume that
- /// they form a CNAME chain and search through them. Possible outcomes
- /// are:
- ///
- /// a) All CNAMES and they form a chain. The result is a referral.
- /// b) All but one are CNAMES and they form a chain. The other is pointed
- /// to by the last element of the chain and is the correct QTYPE. The
- /// result is an answer.
- /// c) Having followed the CNAME chain as far as we can, there is one
- /// remaining RRset that is of the wrong type, or there are multiple
- /// RRsets remaining. return the EXTRADATA code.
- ///
- /// \param qname Question name we are searching for
- /// \param qtype Question type we are search for. (This is assumed not
- /// to be "ANY".)
- /// \param ansrrset Vector of RRsetPtr pointing to the RRsets we are
- /// considering.
- /// \param present Array of "int" the same size of ansrrset, with each
- /// element set to "1" to allow the corresponding element of ansrrset to
- /// be checked, and "0" to skip it. This might be premature optimisation,
- /// but the algorithm would otherwise involve duplicating the RRset
- /// vector then removing elements from random positions one by one. As
- /// each removal involves the destruction of an "xxxPtr" element (which
- /// presently is implemented by boost::shared_ptr), the overhad of memory
- /// management seemed high. This solution imposes some additional loop
- /// cycles, but that should be minimal compared with the overhead of the
- /// memory management.
- /// \param cname_target If the message contains an (unfinished) CNAME
- /// chain, this Name will be replaced by the target of the last CNAME
- /// in the chain
- /// \param cname_count This unsigned int will be incremented with
- /// the number of CNAMEs followed
- /// \param size Number of elements to check. See description of \c present
- /// for details.
- static Category cnameChase(const isc::dns::Name& qname,
- const isc::dns::RRType& qtype,
- isc::dns::Name& cname_target, unsigned int& cname_count,
- std::vector<isc::dns::RRsetPtr>& ansrrset, std::vector<int>& present,
- size_t size);
-};
-
-} // namespace resolve
-} // namespace isc
-
-#endif // RESPONSE_CLASSIFIER_H
+++ /dev/null
-/run_unittests
+++ /dev/null
-AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
-
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
-CLEANFILES = *.gcno *.gcda
-
-TESTS_ENVIRONMENT = \
- $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
-
-TESTS =
-if HAVE_GTEST
-TESTS += run_unittests
-
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-
-run_unittests_SOURCES = run_unittests.cc
-run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
-run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
-run_unittests_SOURCES += resolve_unittest.cc
-run_unittests_SOURCES += resolver_callback_unittest.cc
-run_unittests_SOURCES += response_classifier_unittest.cc
-run_unittests_SOURCES += recursive_query_unittest.cc
-run_unittests_SOURCES += recursive_query_unittest_2.cc
-run_unittests_SOURCES += recursive_query_unittest_3.cc
-
-run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libb10-nsas.la
-run_unittests_LDADD += $(top_builddir)/src/lib/cache/libb10-cache.la
-run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
-run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
-run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libb10-resolve.la
-run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
-run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
-
-endif
-
-noinst_PROGRAMS = $(TESTS)
+++ /dev/null
-// 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 <sys/types.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-
-#include <cstring>
-
-#include <boost/noncopyable.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/bind.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
-
-#include <gtest/gtest.h>
-
-#include <exceptions/exceptions.h>
-
-#include <dns/tests/unittest_util.h>
-#include <util/unittests/wiredata.h>
-#include <dns/rcode.h>
-
-#include <util/buffer.h>
-#include <util/unittests/resolver.h>
-#include <dns/message.h>
-#include <dns/rdataclass.h>
-
-#include <nsas/nameserver_address_store.h>
-#include <cache/resolver_cache.h>
-#include <resolve/resolve.h>
-
-// IMPORTANT: We shouldn't directly use ASIO definitions in this test.
-// In particular, we must not include asio.hpp in this file.
-// The asiolink module is primarily intended to be a wrapper that hide the
-// details of the underlying implementations. We need to test the wrapper
-// level behaviors. In addition, some compilers reject to compile this file
-// if we include asio.hpp unless we specify a special compiler option.
-// If we need to test something at the level of underlying ASIO and need
-// their definition, that test should go to asiolink/internal/tests.
-#include <resolve/recursive_query.h>
-#include <asiodns/dns_lookup.h>
-#include <asiolink/io_socket.h>
-#include <asiolink/io_service.h>
-#include <asiolink/io_message.h>
-#include <asiolink/io_error.h>
-
-using namespace std;
-using namespace isc::asiodns;
-using namespace isc::asiolink;
-using namespace isc::dns;
-using namespace isc::util;
-using isc::UnitTestUtil;
-using isc::util::unittests::matchWireData;
-using boost::scoped_ptr;
-
-namespace isc {
-namespace asiodns {
-
-// This is defined in recursive_query.cc, but not in header (it's not public
-// function). So bring it in to be tested.
-std::string
-deepestDelegation(Name name, RRClass rrclass,
- isc::cache::ResolverCache& cache);
-
-}
-}
-
-namespace {
-const char* const TEST_SERVER_PORT = "53535";
-const char* const TEST_CLIENT_PORT = "53536";
-const char* const TEST_IPV6_ADDR = "::1";
-const char* const TEST_IPV4_ADDR = "127.0.0.1";
-// This data is intended to be valid as a DNS/TCP-like message: the first
-// two octets encode the length of the rest of the data. This is crucial
-// for the tests below.
-const uint8_t test_data[] = {0, 4, 1, 2, 3, 4};
-
-// This function returns an addrinfo structure for use by tests.
-struct addrinfo*
-resolveAddress(const int protocol, const char* const addr,
- const char* const port)
-{
- struct addrinfo hints;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC; // let the address decide it.
- hints.ai_socktype = (protocol == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
- hints.ai_protocol = protocol;
- hints.ai_flags = AI_NUMERICSERV;
-
- struct addrinfo* res;
- const int error = getaddrinfo(addr, port, &hints, &res);
- if (error != 0) {
- isc_throw(IOError, "getaddrinfo failed: " << gai_strerror(error));
- }
-
- return (res);
-}
-
-// convenience shortcut of the other version using different addresses and
-// ports depending on whether we're testing IPv4 or v6, TCP or UDP, and
-// client or server operation.
-struct addrinfo*
-resolveAddress(const int family, const int protocol, const bool client) {
- return (resolveAddress(protocol,
- (family == AF_INET6) ? TEST_IPV6_ADDR :
- TEST_IPV4_ADDR,
- client ? TEST_CLIENT_PORT : TEST_SERVER_PORT));
-}
-
-// A helper holder of addrinfo so we can safely release the resource
-// either when leaving the defined scope either normally or due to exception.
-struct ScopedAddrInfo {
- ScopedAddrInfo(struct addrinfo* res) : res_(res) {}
- ~ScopedAddrInfo() { freeaddrinfo(res_); }
- struct addrinfo* res_;
-};
-
-// Similar to ScopedAddrInfo but for socket FD. It also supports the "release"
-// operation so it can release the ownership of the FD.
-// This is made non copyable to avoid making an accidental copy, which could
-// result in duplicate close.
-struct ScopedSocket : private boost::noncopyable {
- ScopedSocket() : s_(-1) {}
- ScopedSocket(int s) : s_(s) {}
- ~ScopedSocket() {
- if (s_ >= 0) {
- close(s_);
- }
- }
- void reset(int new_s) {
- if (s_ >= 0) {
- close(s_);
- }
- s_ = new_s;
- }
- int release() {
- int s = s_;
- s_ = -1;
- return (s);
- }
- int s_;
-};
-
-// This fixture is a framework for various types of network operations
-// using the ASIO interfaces. Each test case creates an IOService object,
-// opens a local "client" socket for testing, sends data via the local socket
-// to the service that would run in the IOService object.
-// A mock callback function (an ASIOCallBack object) is registered with the
-// IOService object, so the test code should be able to examine the data
-// received on the server side. It then checks the received data matches
-// expected parameters.
-// If initialization parameters of the IOService should be modified, the test
-// case can do it using the setDNSService() method.
-// Note: the set of tests in RecursiveQueryTest use actual network services and may
-// involve undesirable side effects such as blocking.
-class RecursiveQueryTest : public ::testing::Test {
-protected:
- RecursiveQueryTest();
- ~RecursiveQueryTest() {
- // It would delete itself, but after the io_service_, which could
- // segfailt in case there were unhandled requests
- resolver_.reset();
- // In a similar note, we wait until the resolver has been cleaned up
- // until deleting and active test running_query_
- delete running_query_;
- }
-
- void SetUp() {
- callback_.reset(new ASIOCallBack(this));
- }
-
- // Send a test UDP packet to a mock server
- void sendUDP(const int family) {
- ScopedAddrInfo sai(resolveAddress(family, IPPROTO_UDP, false));
- struct addrinfo* res = sai.res_;
-
- sock_.reset(socket(res->ai_family, res->ai_socktype,
- res->ai_protocol));
- if (sock_.s_ < 0) {
- isc_throw(IOError, "failed to open test socket");
- }
- const int cc = sendto(sock_.s_, test_data, sizeof(test_data), 0,
- res->ai_addr, res->ai_addrlen);
- if (cc != sizeof(test_data)) {
- isc_throw(IOError, "unexpected sendto result: " << cc);
- }
- io_service_.run();
- }
-
- // Send a test TCP packet to a mock server
- void sendTCP(const int family) {
- ScopedAddrInfo sai(resolveAddress(family, IPPROTO_TCP, false));
- struct addrinfo* res = sai.res_;
-
- sock_.reset(socket(res->ai_family, res->ai_socktype,
- res->ai_protocol));
- if (sock_.s_ < 0) {
- isc_throw(IOError, "failed to open test socket");
- }
- if (connect(sock_.s_, res->ai_addr, res->ai_addrlen) < 0) {
- isc_throw(IOError, "failed to connect to the test server");
- }
- const int cc = send(sock_.s_, test_data, sizeof(test_data), 0);
- if (cc != sizeof(test_data)) {
- isc_throw(IOError, "unexpected send result: " << cc);
- }
- io_service_.run();
- }
-
- // Receive a UDP packet from a mock server; used for testing
- // recursive lookup. The caller must place a RecursiveQuery
- // on the IO Service queue before running this routine.
- void recvUDP(const int family, void* buffer, size_t& size) {
- ScopedAddrInfo sai(resolveAddress(family, IPPROTO_UDP, true));
- struct addrinfo* res = sai.res_;
-
- sock_.reset(socket(res->ai_family, res->ai_socktype,
- res->ai_protocol));
- if (sock_.s_ < 0) {
- isc_throw(IOError, "failed to open test socket");
- }
-
- if (bind(sock_.s_, res->ai_addr, res->ai_addrlen) < 0) {
- isc_throw(IOError, "bind failed: " << strerror(errno));
- }
-
- // The IO service queue should have a RecursiveQuery object scheduled
- // to run at this point. This call will cause it to begin an
- // async send, then return.
- io_service_.run_one();
-
- // ... and this one will block until the send has completed
- io_service_.run_one();
-
- // Now we attempt to recv() whatever was sent.
- // XXX: there's no guarantee the receiving socket can immediately get
- // the packet. Normally we can perform blocking recv to wait for it,
- // but in theory it's even possible that the packet is lost.
- // In order to prevent the test from hanging in such a worst case
- // we add an ad hoc timeout.
- const struct timeval timeo = { 10, 0 };
- int recv_options = 0;
- if (setsockopt(sock_.s_, SOL_SOCKET, SO_RCVTIMEO, &timeo,
- sizeof(timeo))) {
- if (errno == ENOPROTOOPT) {
- // Workaround for Solaris: it doesn't accept SO_RCVTIMEO
- // with the error of ENOPROTOOPT. Since this is a workaround
- // for rare error cases anyway, we simply switch to the
- // "don't wait" mode. If we still find an error in recv()
- // can happen often we'll consider a more complete solution.
- recv_options = MSG_DONTWAIT;
- } else {
- isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
- }
- }
- const int ret = recv(sock_.s_, buffer, size, recv_options);
- if (ret < 0) {
- isc_throw(IOError, "recvfrom failed: " << strerror(errno));
- }
-
- // Pass the message size back via the size parameter
- size = ret;
- }
-
- void
- addServer(const string& address, const char* const port, int protocol) {
- ScopedAddrInfo sai(resolveAddress(protocol, address.c_str(), port));
- struct addrinfo* res = sai.res_;
- const int family = res->ai_family;
-
- ScopedSocket sock(socket(res->ai_family, res->ai_socktype,
- res->ai_protocol));
- const int s = sock.s_;
- if (s < 0) {
- isc_throw(isc::Unexpected, "failed to open a test socket");
- }
- const int on = 1;
- if (family == AF_INET6) {
- if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) ==
- -1) {
- isc_throw(isc::Unexpected,
- "failed to set socket option(IPV6_V6ONLY)");
- }
- }
- if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
- isc_throw(isc::Unexpected,
- "failed to set socket option(SO_REUSEADDR)");
- }
- if (bind(s, res->ai_addr, res->ai_addrlen) != 0) {
- isc_throw(isc::Unexpected, "failed to bind a test socket");
- }
- if (protocol == IPPROTO_TCP) {
- dns_service_->addServerTCPFromFD(sock.release(), family);
- } else {
- dns_service_->addServerUDPFromFD(sock.release(), family);
- }
- }
-
- // Set up an IO Service queue using the specified address
- void setDNSService(const string& address) {
- setDNSService();
- addServer(address, TEST_SERVER_PORT, IPPROTO_TCP);
- addServer(address, TEST_SERVER_PORT, IPPROTO_UDP);
- }
-
- // Set up an IO Service queue using the "any" address, on IPv4 if
- // 'use_ipv4' is true and on IPv6 if 'use_ipv6' is true.
- void setDNSService(const bool use_ipv4, const bool use_ipv6) {
- setDNSService();
- if (use_ipv6) {
- addServer("::", TEST_SERVER_PORT, IPPROTO_TCP);
- addServer("::", TEST_SERVER_PORT, IPPROTO_UDP);
- }
- if (use_ipv4) {
- addServer("0.0.0.0", TEST_SERVER_PORT, IPPROTO_TCP);
- addServer("0.0.0.0", TEST_SERVER_PORT, IPPROTO_UDP);
- }
- }
-
- // Set up empty DNS Service
- // Set up an IO Service queue without any addresses
- void setDNSService() {
- dns_service_.reset(new DNSService(io_service_, callback_.get(), NULL));
- }
-
- // Run a simple server test, on either IPv4 or IPv6, and over either
- // UDP or TCP. Calls the sendUDP() or sendTCP() methods, which will
- // start the IO Service queue. The UDPServer or TCPServer that was
- // created by setIOService() will receive the test packet and issue a
- // callback, which enables us to check that the data it received
- // matches what we sent.
- void doTest(const int family, const int protocol) {
- if (protocol == IPPROTO_UDP) {
- sendUDP(family);
- } else {
- sendTCP(family);
- }
-
- // There doesn't seem to be an effective test for the validity of
- // 'native'.
- // One thing we are sure is it must be different from our local socket.
- EXPECT_NE(sock_.s_, callback_native_);
- EXPECT_EQ(protocol, callback_protocol_);
- EXPECT_EQ(family == AF_INET6 ? TEST_IPV6_ADDR : TEST_IPV4_ADDR,
- callback_address_);
-
- const uint8_t* expected_data =
- protocol == IPPROTO_UDP ? test_data : test_data + 2;
- const size_t expected_datasize =
- protocol == IPPROTO_UDP ? sizeof(test_data) :
- sizeof(test_data) - 2;
- matchWireData(expected_data, expected_datasize,
- &callback_data_[0], callback_data_.size());
- }
-
-protected:
- // This is a nonfunctional mockup of a DNSServer object. Its purpose
- // is to resume after a recursive query or other asynchronous call
- // has completed.
- class MockServer : public DNSServer {
- public:
- explicit MockServer(IOService& io_service,
- SimpleCallback* checkin = NULL,
- DNSLookup* lookup = NULL,
- DNSAnswer* answer = NULL) :
- io_(io_service),
- message_(new Message(Message::PARSE)),
- answer_message_(new Message(Message::RENDER)),
- respbuf_(new OutputBuffer(0)),
- checkin_(checkin), lookup_(lookup), answer_(answer)
- {}
-
- void operator()(asio::error_code ec = asio::error_code(),
- size_t length = 0)
- {}
-
- void resume(const bool) {
- // should never be called in our tests
- }
-
- DNSServer* clone() {
- MockServer* s = new MockServer(*this);
- return (s);
- }
-
- inline void asyncLookup() {
- if (lookup_) {
- (*lookup_)(*io_message_, message_, answer_message_,
- respbuf_, this);
- }
- }
-
- protected:
- IOService& io_;
-
- private:
- // Currently unused; these will be used for testing
- // asynchronous lookup calls via the asyncLookup() method
- boost::shared_ptr<isc::asiolink::IOMessage> io_message_;
- isc::dns::MessagePtr message_;
- isc::dns::MessagePtr answer_message_;
- isc::util::OutputBufferPtr respbuf_;
-
- // Callback functions provided by the caller
- const SimpleCallback* checkin_;
- const DNSLookup* lookup_;
- const DNSAnswer* answer_;
- };
-
- // This version of mock server just stops the io_service when it is resumed
- class MockServerStop : public MockServer {
- public:
- explicit MockServerStop(IOService& io_service, bool* done) :
- MockServer(io_service),
- done_(done)
- {}
-
- void resume(const bool done) {
- *done_ = done;
- io_.stop();
- }
-
- DNSServer* clone() {
- return (new MockServerStop(*this));
- }
- private:
- bool* done_;
- };
-
- // This version of mock server just stops the io_service when it is resumed
- // the second time. (Used in the clientTimeout test, where resume
- // is called initially with the error answer, and later when the
- // lookup times out, it is called without an answer to send back)
- class MockServerStop2 : public MockServer {
- public:
- explicit MockServerStop2(IOService& io_service,
- bool* done1, bool* done2) :
- MockServer(io_service),
- done1_(done1),
- done2_(done2),
- stopped_once_(false)
- {}
-
- void resume(const bool done) {
- if (stopped_once_) {
- *done2_ = done;
- io_.stop();
- } else {
- *done1_ = done;
- stopped_once_ = true;
- }
- }
-
- DNSServer* clone() {
- return (new MockServerStop2(*this));
- }
- private:
- bool* done1_;
- bool* done2_;
- bool stopped_once_;
- };
-
-private:
- class ASIOCallBack : public DNSLookup {
- public:
- ASIOCallBack(RecursiveQueryTest* test_obj) : test_obj_(test_obj) {}
- void operator()(const IOMessage& io_message,
- isc::dns::MessagePtr, isc::dns::MessagePtr,
- isc::util::OutputBufferPtr, DNSServer*) const {
- test_obj_->callBack(io_message);
- }
- private:
- RecursiveQueryTest* test_obj_;
- };
- void callBack(const IOMessage& io_message) {
- callback_protocol_ = io_message.getSocket().getProtocol();
- callback_native_ = io_message.getSocket().getNative();
- callback_address_ =
- io_message.getRemoteEndpoint().getAddress().toText();
- callback_data_.assign(
- static_cast<const uint8_t*>(io_message.getData()),
- static_cast<const uint8_t*>(io_message.getData()) +
- io_message.getDataSize());
- io_service_.stop();
- }
-protected:
- IOService io_service_;
- scoped_ptr<DNSService> dns_service_;
- scoped_ptr<isc::nsas::NameserverAddressStore> nsas_;
- isc::cache::ResolverCache cache_;
- scoped_ptr<ASIOCallBack> callback_;
- int callback_protocol_;
- int callback_native_;
- string callback_address_;
- vector<uint8_t> callback_data_;
- ScopedSocket sock_;
- boost::shared_ptr<isc::util::unittests::TestResolver> resolver_;
- AbstractRunningQuery* running_query_;
-};
-
-RecursiveQueryTest::RecursiveQueryTest() :
- dns_service_(NULL), callback_(NULL), callback_protocol_(0),
- callback_native_(-1), resolver_(new isc::util::unittests::TestResolver()),
- running_query_(NULL)
-{
- nsas_.reset(new isc::nsas::NameserverAddressStore(resolver_));
-}
-
-TEST_F(RecursiveQueryTest, v6UDPSend) {
- setDNSService(true, true);
- doTest(AF_INET6, IPPROTO_UDP);
-}
-
-TEST_F(RecursiveQueryTest, v6TCPSend) {
- setDNSService(true, true);
- doTest(AF_INET6, IPPROTO_TCP);
-}
-
-TEST_F(RecursiveQueryTest, v4UDPSend) {
- setDNSService(true, true);
- doTest(AF_INET, IPPROTO_UDP);
-}
-
-TEST_F(RecursiveQueryTest, v4TCPSend) {
- setDNSService(true, true);
- doTest(AF_INET, IPPROTO_TCP);
-}
-
-TEST_F(RecursiveQueryTest, v6UDPSendSpecific) {
- // Explicitly set a specific address to be bound to the socket.
- // The subsequent test does not directly ensures the underlying socket
- // is bound to the expected address, but the success of the tests should
- // reasonably suggest it works as intended.
- // Specifying an address also implicitly means the service runs in a
- // single address-family mode. In tests using TCP we can confirm that
- // by trying to make a connection and seeing a failure. In UDP, it'd be
- // more complicated because we need to use a connected socket and catch
- // an error on a subsequent read operation. We could do it, but for
- // simplicity we only tests the easier cases for now.
-
- setDNSService(TEST_IPV6_ADDR);
- doTest(AF_INET6, IPPROTO_UDP);
-}
-
-TEST_F(RecursiveQueryTest, v6TCPSendSpecific) {
- setDNSService(TEST_IPV6_ADDR);
- doTest(AF_INET6, IPPROTO_TCP);
-
- EXPECT_THROW(sendTCP(AF_INET), IOError);
-}
-
-TEST_F(RecursiveQueryTest, v4UDPSendSpecific) {
- setDNSService(TEST_IPV4_ADDR);
- doTest(AF_INET, IPPROTO_UDP);
-}
-
-TEST_F(RecursiveQueryTest, v4TCPSendSpecific) {
- setDNSService(TEST_IPV4_ADDR);
- doTest(AF_INET, IPPROTO_TCP);
-
- EXPECT_THROW(sendTCP(AF_INET6), IOError);
-}
-
-TEST_F(RecursiveQueryTest, v6AddServer) {
- setDNSService();
- addServer(TEST_IPV6_ADDR, TEST_SERVER_PORT, IPPROTO_TCP);
- doTest(AF_INET6, IPPROTO_TCP);
-
- EXPECT_THROW(sendTCP(AF_INET), IOError);
-}
-
-TEST_F(RecursiveQueryTest, v4AddServer) {
- setDNSService();
- addServer(TEST_IPV4_ADDR, TEST_SERVER_PORT, IPPROTO_TCP);
- doTest(AF_INET, IPPROTO_TCP);
-
- EXPECT_THROW(sendTCP(AF_INET6), IOError);
-}
-
-TEST_F(RecursiveQueryTest, clearServers) {
- setDNSService();
- dns_service_->clearServers();
-
- EXPECT_THROW(sendTCP(AF_INET), IOError);
- EXPECT_THROW(sendTCP(AF_INET6), IOError);
-}
-
-TEST_F(RecursiveQueryTest, v6TCPOnly) {
- // Open only IPv6 TCP socket. A subsequent attempt of establishing an
- // IPv4/TCP connection should fail. See above for why we only test this
- // for TCP.
- setDNSService(false, true);
- EXPECT_THROW(sendTCP(AF_INET), IOError);
-}
-
-TEST_F(RecursiveQueryTest, v4TCPOnly) {
- setDNSService(true, false);
- EXPECT_THROW(sendTCP(AF_INET6), IOError);
-}
-
-vector<pair<string, uint16_t> >
-singleAddress(const string &address, uint16_t port) {
- vector<pair<string, uint16_t> > result;
- result.push_back(pair<string, uint16_t>(address, port));
- return (result);
-}
-
-TEST_F(RecursiveQueryTest, recursiveSetupV4) {
- setDNSService(true, false);
- uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
- EXPECT_NO_THROW(RecursiveQuery(*dns_service_,
- *nsas_, cache_,
- singleAddress(TEST_IPV4_ADDR, port),
- singleAddress(TEST_IPV4_ADDR, port)));
-}
-
-TEST_F(RecursiveQueryTest, recursiveSetupV6) {
- setDNSService(false, true);
- uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
- EXPECT_NO_THROW(RecursiveQuery(*dns_service_,
- *nsas_, cache_,
- singleAddress(TEST_IPV6_ADDR, port),
- singleAddress(TEST_IPV6_ADDR,port)));
-}
-
-// XXX:
-// This is very inadequate unit testing. It should be generalized into
-// a routine that can do this with variable address family, address, and
-// port, and with the various callbacks defined in such a way as to ensure
-// full code coverage including error cases.
-TEST_F(RecursiveQueryTest, forwarderSend) {
- setDNSService(true, false);
-
- // Note: We use the test prot plus one to ensure we aren't binding
- // to the same port as the actual server
- uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
-
- MockServer server(io_service_);
- RecursiveQuery rq(*dns_service_,
- *nsas_, cache_,
- singleAddress(TEST_IPV4_ADDR, port),
- singleAddress(TEST_IPV4_ADDR, port));
-
- Question q(Name("example.com"), RRClass::IN(), RRType::TXT());
- MessagePtr query_message(new Message(Message::RENDER));
- isc::resolve::initResponseMessage(q, *query_message);
-
- OutputBufferPtr buffer(new OutputBuffer(0));
- MessagePtr answer(new Message(Message::RENDER));
- running_query_ = rq.forward(query_message, answer, buffer, &server);
-
- char data[4096];
- size_t size = sizeof(data);
- ASSERT_NO_THROW(recvUDP(AF_INET, data, size));
-
- Message m(Message::PARSE);
- InputBuffer ibuf(data, size);
-
- // Make sure we can parse the message that was sent
- EXPECT_NO_THROW(m.parseHeader(ibuf));
- EXPECT_NO_THROW(m.fromWire(ibuf));
-
- // Check that the question sent matches the one we wanted
- QuestionPtr q2 = *m.beginQuestion();
- EXPECT_EQ(q.getName(), q2->getName());
- EXPECT_EQ(q.getType(), q2->getType());
- EXPECT_EQ(q.getClass(), q2->getClass());
-}
-
-int
-createTestSocket() {
- ScopedAddrInfo sai(resolveAddress(AF_INET, IPPROTO_UDP, true));
- struct addrinfo* res = sai.res_;
-
- ScopedSocket sock(socket(res->ai_family, res->ai_socktype,
- res->ai_protocol));
- if (sock.s_ < 0) {
- isc_throw(IOError, "failed to open test socket");
- }
- if (bind(sock.s_, res->ai_addr, res->ai_addrlen) < 0) {
- isc_throw(IOError, "failed to bind test socket");
- }
- return (sock.release());
-}
-
-// Mock resolver callback for testing forward query.
-class MockResolverCallback : public isc::resolve::ResolverInterface::Callback {
-public:
- enum ResultValue {
- DEFAULT = 0,
- SUCCESS = 1,
- FAILURE = 2
- };
-
- MockResolverCallback(DNSServer* server):
- result(DEFAULT),
- server_(server->clone())
- {}
-
- ~MockResolverCallback() {
- delete server_;
- }
-
- void success(const isc::dns::MessagePtr response) {
- result = SUCCESS;
- server_->resume(true);
- }
-
- void failure() {
- result = FAILURE;
- server_->resume(false);
- }
-
- uint32_t result;
-private:
- DNSServer* server_;
-};
-
-// Test query timeout, set query timeout is lower than client timeout
-// and lookup timeout.
-TEST_F(RecursiveQueryTest, forwardQueryTimeout) {
- // Prepare the service (we do not use the common setup, we do not answer
- setDNSService();
-
- // Prepare the socket
- sock_.reset(createTestSocket());
-
- // Prepare the server
- bool done(true);
- MockServerStop server(io_service_, &done);
-
- // Do the answer
- const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
- RecursiveQuery query(*dns_service_,
- *nsas_, cache_,
- singleAddress(TEST_IPV4_ADDR, port),
- singleAddress(TEST_IPV4_ADDR, port),
- 10, 4000, 3000, 2);
- Question question(Name("example.net"), RRClass::IN(), RRType::A());
- OutputBufferPtr buffer(new OutputBuffer(0));
- MessagePtr answer(new Message(Message::RENDER));
- MessagePtr query_message(new Message(Message::RENDER));
- isc::resolve::initResponseMessage(question, *query_message);
-
- boost::shared_ptr<MockResolverCallback> callback(new MockResolverCallback(&server));
- running_query_ = query.forward(query_message, answer, buffer, &server, callback);
- // Run the test
- io_service_.run();
- EXPECT_EQ(callback->result, MockResolverCallback::FAILURE);
-}
-
-// If we set client timeout to lower than querytimeout, we should
-// get a failure answer
-// (no actual answer is given here yet. TODO the returned error message
-// should be tested)
-TEST_F(RecursiveQueryTest, forwardClientTimeout) {
- // Prepare the service (we do not use the common setup, we do not answer
- setDNSService();
-
- sock_.reset(createTestSocket());
-
- // Prepare the server
- bool done1(true);
- MockServerStop server(io_service_, &done1);
-
- MessagePtr answer(new Message(Message::RENDER));
-
- // Do the answer
- const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
- RecursiveQuery query(*dns_service_,
- *nsas_, cache_,
- singleAddress(TEST_IPV4_ADDR, port),
- singleAddress(TEST_IPV4_ADDR, port),
- 1000, 10, 4000, 4);
- Question q(Name("example.net"), RRClass::IN(), RRType::A());
- OutputBufferPtr buffer(new OutputBuffer(0));
- MessagePtr query_message(new Message(Message::RENDER));
- isc::resolve::initResponseMessage(q, *query_message);
-
- boost::shared_ptr<MockResolverCallback> callback(new MockResolverCallback(&server));
- running_query_ = query.forward(query_message, answer, buffer, &server, callback);
- // Run the test
- io_service_.run();
- EXPECT_EQ(callback->result, MockResolverCallback::FAILURE);
-}
-
-// If we set lookup timeout to lower than querytimeout, the lookup
-// will fail.
-TEST_F(RecursiveQueryTest, forwardLookupTimeout) {
- // Prepare the service (we do not use the common setup, we do not answer
- setDNSService();
-
- // Prepare the socket
- sock_.reset(createTestSocket());
-
- // Prepare the server
- bool done(true);
- MockServerStop server(io_service_, &done);
-
- MessagePtr answer(new Message(Message::RENDER));
-
- // Do the answer
- const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
- RecursiveQuery query(*dns_service_,
- *nsas_, cache_,
- singleAddress(TEST_IPV4_ADDR, port),
- singleAddress(TEST_IPV4_ADDR, port),
- 1000, 4000, 10, 5);
- Question question(Name("example.net"), RRClass::IN(), RRType::A());
- OutputBufferPtr buffer(new OutputBuffer(0));
-
- //Message query_message(Message::RENDER);
- MessagePtr query_message(new Message(Message::RENDER));
- isc::resolve::initResponseMessage(question, *query_message);
-
- boost::shared_ptr<MockResolverCallback> callback(new MockResolverCallback(&server));
- running_query_ = query.forward(query_message, answer, buffer, &server, callback);
- // Run the test
- io_service_.run();
- EXPECT_EQ(callback->result, MockResolverCallback::FAILURE);
-}
-
-// Set everything very low and see if this doesn't cause weird
-// behaviour
-TEST_F(RecursiveQueryTest, lowtimeouts) {
- // Prepare the service (we do not use the common setup, we do not answer
- setDNSService();
-
- // Prepare the socket
- sock_.reset(createTestSocket());
-
- // Prepare the server
- bool done(true);
- MockServerStop server(io_service_, &done);
-
- MessagePtr answer(new Message(Message::RENDER));
-
- // Do the answer
- const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
- RecursiveQuery query(*dns_service_,
- *nsas_, cache_,
- singleAddress(TEST_IPV4_ADDR, port),
- singleAddress(TEST_IPV4_ADDR, port),
- 1, 1, 1, 1);
- Question question(Name("example.net"), RRClass::IN(), RRType::A());
- OutputBufferPtr buffer(new OutputBuffer(0));
-
- MessagePtr query_message(new Message(Message::RENDER));
- isc::resolve::initResponseMessage(question, *query_message);
-
- boost::shared_ptr<MockResolverCallback> callback(new MockResolverCallback(&server));
- running_query_ = query.forward(query_message, answer, buffer, &server, callback);
- // Run the test
- io_service_.run();
- EXPECT_EQ(callback->result, MockResolverCallback::FAILURE);
-}
-
-// as mentioned above, we need a more better framework for this,
-// in addition to that, this sends out queries into the world
-// (which we should catch somehow and fake replies for)
-// for the skeleton code, it shouldn't be too much of a problem
-// Ok so even we don't all have access to the DNS world right now,
-// so disabling these tests too.
-TEST_F(RecursiveQueryTest, DISABLED_recursiveSendOk) {
- setDNSService(true, false);
- bool done;
-
- MockServerStop server(io_service_, &done);
- vector<pair<string, uint16_t> > empty_vector;
- RecursiveQuery rq(*dns_service_, *nsas_, cache_, empty_vector,
- empty_vector, 10000, 0);
-
- Question q(Name("www.isc.org"), RRClass::IN(), RRType::A());
- OutputBufferPtr buffer(new OutputBuffer(0));
- MessagePtr answer(new Message(Message::RENDER));
- running_query_ = rq.resolve(q, answer, buffer, &server);
- io_service_.run();
-
- // Check that the answer we got matches the one we wanted
- EXPECT_EQ(Rcode::NOERROR(), answer->getRcode());
- ASSERT_EQ(1, answer->getRRCount(Message::SECTION_ANSWER));
- RRsetPtr a = *answer->beginSection(Message::SECTION_ANSWER);
- EXPECT_EQ(q.getName(), a->getName());
- EXPECT_EQ(q.getType(), a->getType());
- EXPECT_EQ(q.getClass(), a->getClass());
- EXPECT_EQ(1, a->getRdataCount());
-}
-
-// see comments at previous test
-TEST_F(RecursiveQueryTest, DISABLED_recursiveSendNXDOMAIN) {
- setDNSService(true, false);
- bool done;
-
- MockServerStop server(io_service_, &done);
- vector<pair<string, uint16_t> > empty_vector;
- RecursiveQuery rq(*dns_service_, *nsas_, cache_, empty_vector,
- empty_vector, 10000, 0);
-
- Question q(Name("wwwdoesnotexist.isc.org"), RRClass::IN(), RRType::A());
- OutputBufferPtr buffer(new OutputBuffer(0));
- MessagePtr answer(new Message(Message::RENDER));
- running_query_ = rq.resolve(q, answer, buffer, &server);
- io_service_.run();
-
- // Check that the answer we got matches the one we wanted
- EXPECT_EQ(Rcode::NXDOMAIN(), answer->getRcode());
- EXPECT_EQ(0, answer->getRRCount(Message::SECTION_ANSWER));
-}
-
-// Test that we don't start at root when we have a lower NS cached.
-TEST_F(RecursiveQueryTest, CachedNS) {
- setDNSService(true, true);
-
- // Check we have a reasonable fallback - if there's nothing of interest
- // in the cache, start at root.
- EXPECT_EQ(".", deepestDelegation(Name("www.somewhere.deep.example.org"),
- RRClass::IN(), cache_));
-
- // Prefill the cache. There's a zone with a NS and IP address for one
- // of them (to see that one is enough) and another deeper one, with NS,
- // but without IP.
- RRsetPtr nsUpper(new RRset(Name("example.org"), RRClass::IN(),
- RRType::NS(), RRTTL(300)));
- nsUpper->addRdata(rdata::generic::NS(Name("ns.example.org")));
- nsUpper->addRdata(rdata::generic::NS(Name("ns2.example.org")));
-
- RRsetPtr nsLower(new RRset(Name("somewhere.deep.example.org"),
- RRClass::IN(), RRType::NS(), RRTTL(300)));
- nsLower->addRdata(rdata::generic::NS(Name("ns.somewhere.deep.example.org"))
- );
-
- RRsetPtr nsIp(new RRset(Name("ns2.example.org"), RRClass::IN(),
- RRType::A(), RRTTL(300)));
- nsIp->addRdata(rdata::in::A("192.0.2.1"));
-
- // Make sure the test runs in the correct environment (we don't test
- // the cache, but we need it to unswer this way for the test, so we
- // just make sure)
- ASSERT_TRUE(cache_.update(nsUpper));
- ASSERT_TRUE(cache_.update(nsLower));
- ASSERT_TRUE(cache_.update(nsIp));
- RRsetPtr deepest(cache_.lookupDeepestNS(Name(
- "www.somewhere.deep.example.org"), RRClass::IN()));
- ASSERT_NE(RRsetPtr(), deepest);
- ASSERT_EQ(nsLower->getName(), deepest->getName());
-
- // Direct check of the function that chooses the delegation point
- // It should not use nsLower, because we don't have IP address for
- // that one. But it can choose nsUpper.
- EXPECT_EQ("example.org.",
- deepestDelegation(Name("www.somewhere.deep.example.org"),
- RRClass::IN(), cache_));
-
- // Now more complex and indirect test:
- // We ask it to resolve the name for us. It will pick up a delegation
- // point and ask NSAS for it. NSAS will in turn ask resolver for NS record
- // of the delegation point. We then pick it up from the fake resolver
- // and check it is the correct one. This checks the delegation point
- // travels safely through the whole path there (it would be enough to check
- // it up to NSAS, but replacing NSAS is more complicated, so we just
- // include in the test as well for simplicity).
-
- // Prepare the recursive query
- vector<pair<string, uint16_t> > roots;
- roots.push_back(pair<string, uint16_t>("192.0.2.2", 53));
- vector<pair<string, uint16_t> > upstream;
- RecursiveQuery rq(*dns_service_, *nsas_, cache_,
- upstream, roots);
- // Ask a question at the bottom. It should not use the lower NS, because
- // it would lead to a loop in NS. But it can use the nsUpper one, it has
- // an IP address and we can avoid asking root.
- Question q(Name("www.somewhere.deep.example.org"), RRClass::IN(),
- RRType::A());
- OutputBufferPtr buffer(new OutputBuffer(0));
- MessagePtr answer(new Message(Message::RENDER));
- // The server is here so we have something to pass there
- MockServer server(io_service_);
- running_query_ = rq.resolve(q, answer, buffer, &server);
- // We don't need to run the service in this test. We are interested only
- // in the place it starts resolving at
-
- // Look what is asked by NSAS - it should be our delegation point.
- EXPECT_NO_THROW(EXPECT_EQ(nsUpper->getName(),
- (*resolver_)[0]->getName()) <<
- "It starts resolving at the wrong place") <<
- "It does not ask NSAS anything, how does it know where to send?";
-}
-
-// TODO: add tests that check whether the cache is updated on succesfull
-// responses, and not updated on failures.
-
-}
+++ /dev/null
-// 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 <algorithm>
-#include <cstdlib>
-#include <iomanip>
-#include <iostream>
-#include <string>
-#include <vector>
-
-#include <gtest/gtest.h>
-#include <boost/bind.hpp>
-
-#include <asio.hpp>
-
-#include <util/buffer.h>
-#include <util/io_utilities.h>
-
-#include <dns/question.h>
-#include <dns/message.h>
-#include <dns/messagerenderer.h>
-#include <dns/opcode.h>
-#include <dns/name.h>
-#include <dns/rcode.h>
-#include <dns/rrtype.h>
-#include <dns/rrset.h>
-#include <dns/rrttl.h>
-#include <dns/rdata.h>
-
-#include <util/io_utilities.h>
-#include <asiodns/dns_service.h>
-#include <asiodns/io_fetch.h>
-#include <asiolink/io_address.h>
-#include <asiolink/io_endpoint.h>
-#include <asiolink/io_service.h>
-#include <resolve/recursive_query.h>
-#include <resolve/resolver_interface.h>
-
-using namespace asio;
-using namespace asio::ip;
-using namespace isc::asiolink;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::util;
-using namespace isc::resolve;
-using namespace std;
-
-/// RecursiveQuery Test - 2
-///
-/// The second part of the RecursiveQuery unit tests, this attempts to get the
-/// RecursiveQuery object to follow a set of referrals for "www.example.org" to
-/// and to invoke TCP fallback on one of the queries. In particular, we expect
-/// that the test will do the following in an attempt to resolve
-/// www.example.org:
-///
-/// - Send question over UDP to "root" - get referral to "org".
-/// - Send question over UDP to "org" - get referral to "example.org" with TC bit set.
-/// - Send question over TCP to "org" - get referral to "example.org".
-/// - Send question over UDP to "example.org" - get response for www.example.org.
-///
-/// (The order of queries is set in this way in order to also test that after a
-/// failover to TCP, queries revert to UDP).
-///
-/// By using the "test_server_" element of RecursiveQuery, all queries are
-/// directed to one or other of the "servers" in the RecursiveQueryTest2 class,
-/// regardless of the glue returned in referrals.
-
-namespace {
-const char* const TEST_ADDRESS = "127.0.0.1"; ///< Servers are on this address
-const uint16_t TEST_PORT = 5301; ///< ... and this port
-const size_t BUFFER_SIZE = 1024; ///< For all buffers
-const char* const WWW_EXAMPLE_ORG = "192.0.2.254";
- ///< Address of www.example.org
-} //end anonymous namespace
-
-namespace isc {
-namespace asiodns {
-
-// As the test is fairly long and complex, debugging "print" statements have
-// been left in although they are disabled. Set the following to "true" to
-// enable them.
-const bool DEBUG_PRINT = false;
-
-class MockResolver : public isc::resolve::ResolverInterface {
-public:
- virtual void resolve(const QuestionPtr& question,
- const ResolverInterface::CallbackPtr& callback) {
- }
-
- virtual ~MockResolver() {}
-};
-
-
-
-/// \brief Test fixture for the RecursiveQuery Test
-class RecursiveQueryTest2 : public virtual ::testing::Test
-{
-public:
-
- /// \brief Status of query
- ///
- /// Set before the query and then by each "server" when responding.
- enum QueryStatus {
- NONE = 0, ///< Default
- UDP_ROOT = 1, ///< Query root server over UDP
- UDP_ORG = 2, ///< Query ORG server over UDP
- TCP_ORG = 3, ///< Query ORG server over TCP
- UDP_EXAMPLE_ORG_BAD = 4, ///< Query EXAMPLE.ORG server over UDP
- ///< (return malformed packet)
- UDP_EXAMPLE_ORG = 5, ///< Query EXAMPLE.ORG server over UDP
- COMPLETE = 6 ///< Query is complete
- };
-
- // Common stuff
- bool debug_; ///< Set true for debug print
- IOService service_; ///< Service to run everything
- DNSService dns_service_; ///< Resolver is part of "server"
- QuestionPtr question_; ///< What to ask
- QueryStatus last_; ///< What was the last state
- QueryStatus expected_; ///< Expected next state
- OutputBufferPtr question_buffer_; ///< Question we expect to receive
- boost::shared_ptr<MockResolver> resolver_; ///< Mock resolver
- isc::nsas::NameserverAddressStore* nsas_; ///< Nameserver address store
- isc::cache::ResolverCache cache_; ///< Resolver cache
-
- // Data for TCP Server
- size_t tcp_cumulative_; ///< Cumulative TCP data received
- tcp::endpoint tcp_endpoint_; ///< Endpoint for TCP receives
- size_t tcp_length_; ///< Expected length value
- uint8_t tcp_receive_buffer_[BUFFER_SIZE]; ///< Receive buffer for TCP I/O
- OutputBufferPtr tcp_send_buffer_; ///< Send buffer for TCP I/O
- tcp::socket tcp_socket_; ///< Socket used by TCP server
-
- /// Data for UDP
- udp::endpoint udp_remote_; ///< Endpoint for UDP receives
- size_t udp_length_; ///< Expected length value
- uint8_t udp_receive_buffer_[BUFFER_SIZE]; ///< Receive buffer for UDP I/O
- OutputBufferPtr udp_send_buffer_; ///< Send buffer for UDP I/O
- udp::socket udp_socket_; ///< Socket used by UDP server
-
- /// Some of the tests cause an 'active' running query to be created, but
- /// don't complete the framework that makes that query delete itself.
- /// This member can be used to store it so that it is deleted automatically
- /// when the test is finished.
- AbstractRunningQuery* running_query_;
-
- /// \brief Constructor
- RecursiveQueryTest2() :
- debug_(DEBUG_PRINT),
- service_(),
- dns_service_(service_, NULL, NULL),
- question_(new Question(Name("www.example.org"), RRClass::IN(), RRType::A())),
- last_(NONE),
- expected_(NONE),
- question_buffer_(new OutputBuffer(BUFFER_SIZE)),
- resolver_(new MockResolver()),
- nsas_(new isc::nsas::NameserverAddressStore(resolver_)),
- tcp_cumulative_(0),
- tcp_endpoint_(asio::ip::address::from_string(TEST_ADDRESS), TEST_PORT),
- tcp_length_(0),
- tcp_receive_buffer_(),
- tcp_send_buffer_(new OutputBuffer(BUFFER_SIZE)),
- tcp_socket_(service_.get_io_service()),
- udp_remote_(),
- udp_length_(0),
- udp_receive_buffer_(),
- udp_send_buffer_(new OutputBuffer(BUFFER_SIZE)),
- udp_socket_(service_.get_io_service(), udp::v4()),
- running_query_(NULL)
- {}
-
- ~RecursiveQueryTest2() {
- // It would delete itself, but after the io_service_, which could
- // segfailt in case there were unhandled requests
- resolver_.reset();
- // In a similar note, we wait until the resolver has been cleaned up
- // until deleting and active test running_query_
- delete running_query_;
- delete nsas_;
- }
-
- /// \brief Set Common Message Bits
- ///
- /// Sets up the common bits of a response message returned by the handlers.
- ///
- /// \param msg Message buffer in RENDER mode.
- /// \param qid QID to set the message to
- void setCommonMessage(isc::dns::Message& msg, uint16_t qid = 0) {
- msg.setQid(qid);
- msg.setHeaderFlag(Message::HEADERFLAG_QR);
- msg.setOpcode(Opcode::QUERY());
- msg.setHeaderFlag(Message::HEADERFLAG_AA);
- msg.setRcode(Rcode::NOERROR());
- msg.addQuestion(*question_);
- }
-
- /// \brief Set Referral to "org"
- ///
- /// Sets up the passed-in message (expected to be in "RENDER" mode to
- /// indicate a referral to fictitious .org nameservers.
- ///
- /// \param msg Message to update with referral information.
- void setReferralOrg(isc::dns::Message& msg) {
- if (debug_) {
- cout << "setReferralOrg(): creating referral to .org nameservers" << endl;
- }
-
- // Do a referral to org. We'll define all NS records as "in-zone"
- // nameservers (and supply glue) to avoid the possibility of the
- // resolver starting another recursive query to resolve the address of
- // a nameserver.
- RRsetPtr org_ns(new RRset(Name("org."), RRClass::IN(), RRType::NS(), RRTTL(300)));
- org_ns->addRdata(createRdata(RRType::NS(), RRClass::IN(), "ns1.org."));
- org_ns->addRdata(createRdata(RRType::NS(), RRClass::IN(), "ns2.org."));
- msg.addRRset(Message::SECTION_AUTHORITY, org_ns);
-
- RRsetPtr org_ns1(new RRset(Name("ns1.org."), RRClass::IN(), RRType::A(), RRTTL(300)));
- org_ns1->addRdata(createRdata(RRType::A(), RRClass::IN(), "192.0.2.1"));
- msg.addRRset(Message::SECTION_ADDITIONAL, org_ns1);
-
- RRsetPtr org_ns2(new RRset(Name("ns2.org."), RRClass::IN(), RRType::A(), RRTTL(300)));
- org_ns2->addRdata(createRdata(RRType::A(), RRClass::IN(), "192.0.2.2"));
- msg.addRRset(Message::SECTION_ADDITIONAL, org_ns2);
- }
-
- /// \brief Set Referral to "example.org"
- ///
- /// Sets up the passed-in message (expected to be in "RENDER" mode to
- /// indicate a referral to fictitious example.org nameservers.
- ///
- /// \param msg Message to update with referral information.
- void setReferralExampleOrg(isc::dns::Message& msg) {
- if (debug_) {
- cout << "setReferralExampleOrg(): creating referral to example.org nameservers" << endl;
- }
-
- // Do a referral to example.org. As before, we'll define all NS
- // records as "in-zone" nameservers (and supply glue) to avoid the
- // possibility of the resolver starting another recursive query to look
- // up the address of the nameserver.
- RRsetPtr example_org_ns(new RRset(Name("example.org."), RRClass::IN(), RRType::NS(), RRTTL(300)));
- example_org_ns->addRdata(createRdata(RRType::NS(), RRClass::IN(), "ns1.example.org."));
- example_org_ns->addRdata(createRdata(RRType::NS(), RRClass::IN(), "ns2.example.org."));
- msg.addRRset(Message::SECTION_AUTHORITY, example_org_ns);
-
- RRsetPtr example_org_ns1(new RRset(Name("ns1.example.org."), RRClass::IN(), RRType::A(), RRTTL(300)));
- example_org_ns1->addRdata(createRdata(RRType::A(), RRClass::IN(), "192.0.2.11"));
- msg.addRRset(Message::SECTION_ADDITIONAL, example_org_ns1);
-
- RRsetPtr example_org_ns2(new RRset(Name("ns2.example.org."), RRClass::IN(), RRType::A(), RRTTL(300)));
- example_org_ns2->addRdata(createRdata(RRType::A(), RRClass::IN(), "192.0.2.21"));
- msg.addRRset(Message::SECTION_ADDITIONAL, example_org_ns2);
- }
-
- /// \brief Set Answer to "www.example.org"
- ///
- /// Sets up the passed-in message (expected to be in "RENDER" mode) to
- /// indicate an authoritative answer to www.example.org.
- ///
- /// \param msg Message to update with referral information.
- void setAnswerWwwExampleOrg(isc::dns::Message& msg) {
- if (debug_) {
- cout << "setAnswerWwwExampleOrg(): creating answer for www.example.org" << endl;
- }
-
- // Give a response for www.example.org.
- RRsetPtr www_example_org_a(new RRset(Name("www.example.org."), RRClass::IN(), RRType::A(), RRTTL(300)));
- www_example_org_a->addRdata(createRdata(RRType::A(), RRClass::IN(), WWW_EXAMPLE_ORG));
- msg.addRRset(Message::SECTION_ANSWER, www_example_org_a);
-
- // ... and add the Authority and Additional sections. (These are the
- // same as in the referral to example.org from the .org nameserver.)
- setReferralExampleOrg(msg);
- }
-
- /// \brief UDP Receive Handler
- ///
- /// This is invoked when a message is received over UDP from the
- /// RecursiveQuery object under test. It formats an answer and sends it
- /// asynchronously, with the UdpSendHandler method being specified as the
- /// completion handler.
- ///
- /// \param ec ASIO error code, completion code of asynchronous I/O issued
- /// by the "server" to receive data.
- /// \param length Amount of data received.
- void udpReceiveHandler(asio::error_code ec = asio::error_code(),
- size_t length = 0)
- {
- if (debug_) {
- cout << "udpReceiveHandler(): error = " << ec.value() <<
- ", length = " << length << ", last state = " << last_ <<
- ", expected state = " << expected_ << endl;
- }
-
- // Expected state should be one greater than the last state.
- EXPECT_EQ(static_cast<int>(expected_), static_cast<int>(last_) + 1);
- last_ = expected_;
-
- // The QID in the incoming data is random so set it to 0 for the
- // data comparison check. (It is set to 0 in the buffer containing
- // the expected data.)
- // And check that question we received is what was expected.
- uint16_t qid = checkReceivedPacket(udp_receive_buffer_, length);
-
- // The message returned depends on what state we are in. Set up
- // common stuff first: bits not mentioned are set to 0.
- Message msg(Message::RENDER);
- setCommonMessage(msg, qid);
-
- // In the case of UDP_EXAMPLE_ORG_BAD, we shall mangle the
- // response
- bool mangle_response = false;
-
- // Set up state-dependent bits:
- switch (expected_) {
- case UDP_ROOT:
- // Return a referral to org. We then expect to query the "org"
- // nameservers over UDP next.
- setReferralOrg(msg);
- expected_ = UDP_ORG;
- break;
-
- case UDP_ORG:
- // Return a referral to example.org. We explicitly set the TC bit to
- // force a repeat query to the .org nameservers over TCP.
- setReferralExampleOrg(msg);
- if (debug_) {
- cout << "udpReceiveHandler(): setting TC bit" << endl;
- }
- msg.setHeaderFlag(Message::HEADERFLAG_TC);
- expected_ = TCP_ORG;
- break;
-
- case UDP_EXAMPLE_ORG_BAD:
- // Return the answer to the question.
- setAnswerWwwExampleOrg(msg);
- // Mangle the response to enfore another query
- mangle_response = true;
- expected_ = UDP_EXAMPLE_ORG;
- break;
-
- case UDP_EXAMPLE_ORG:
- // Return the answer to the question.
- setAnswerWwwExampleOrg(msg);
- expected_ = COMPLETE;
- break;
-
- default:
- FAIL() << "UdpReceiveHandler called with unknown state";
- }
-
- // Convert to wire format
- udp_send_buffer_->clear();
- MessageRenderer renderer;
- renderer.setBuffer(udp_send_buffer_.get());
- msg.toWire(renderer);
-
- if (mangle_response) {
- // mangle the packet a bit
- // set additional to one more
- udp_send_buffer_->writeUint8At(3, 11);
- }
-
- // Return a message back to the IOFetch object (after setting the
- // expected length of data for the check in the send handler).
- udp_length_ = udp_send_buffer_->getLength();
- udp_socket_.async_send_to(asio::buffer(udp_send_buffer_->getData(),
- udp_send_buffer_->getLength()),
- udp_remote_,
- boost::bind(&RecursiveQueryTest2::udpSendHandler,
- this, _1, _2));
- }
-
- /// \brief UDP Send Handler
- ///
- /// Called when a send operation of the UDP server (i.e. a response
- /// being sent to the RecursiveQuery) has completed, this re-issues
- /// a read call.
- ///
- /// \param ec Completion error code of the send.
- /// \param length Actual number of bytes sent.
- void udpSendHandler(asio::error_code ec = asio::error_code(),
- size_t length = 0)
- {
- if (debug_) {
- cout << "udpSendHandler(): error = " << ec.value() <<
- ", length = " << length << endl;
- }
-
- // Check send was OK
- EXPECT_EQ(0, ec.value());
- EXPECT_EQ(udp_length_, length);
-
- // Reissue the receive call to await the next message.
- udp_socket_.async_receive_from(
- asio::buffer(udp_receive_buffer_, sizeof(udp_receive_buffer_)),
- udp_remote_,
- boost::bind(&RecursiveQueryTest2::udpReceiveHandler, this, _1, _2));
- }
-
- /// \brief Completion Handler for Accepting TCP Data
- ///
- /// Called when the remote system connects to the "TCP server". It issues
- /// an asynchronous read on the socket to read data.
- ///
- /// \param socket Socket on which data will be received
- /// \param ec Boost error code, value should be zero.
- void tcpAcceptHandler(asio::error_code ec = asio::error_code(),
- size_t length = 0)
- {
- if (debug_) {
- cout << "tcpAcceptHandler(): error = " << ec.value() <<
- ", length = " << length << endl;
- }
-
- // Expect that the accept completed without a problem.
- EXPECT_EQ(0, ec.value());
-
- // Initiate a read on the socket, indicating that nothing has yet been
- // received.
- tcp_cumulative_ = 0;
- tcp_socket_.async_receive(
- asio::buffer(tcp_receive_buffer_, sizeof(tcp_receive_buffer_)),
- boost::bind(&RecursiveQueryTest2::tcpReceiveHandler, this, _1, _2));
- }
-
- /// \brief Completion Handler for Receiving TCP Data
- ///
- /// Reads data from the RecursiveQuery object and loops, reissuing reads,
- /// until all the message has been read. It then returns an appropriate
- /// response.
- ///
- /// \param socket Socket to use to send the answer
- /// \param ec ASIO error code, completion code of asynchronous I/O issued
- /// by the "server" to receive data.
- /// \param length Amount of data received.
- void tcpReceiveHandler(asio::error_code ec = asio::error_code(),
- size_t length = 0)
- {
- if (debug_) {
- cout << "tcpReceiveHandler(): error = " << ec.value() <<
- ", length = " << length <<
- ", cumulative = " << tcp_cumulative_ << endl;
- }
-
- // Expect that the receive completed without a problem.
- EXPECT_EQ(0, ec.value());
-
- // Have we received all the data? We know this by checking if the two-
- // byte length count in the message is equal to the data received.
- tcp_cumulative_ += length;
- bool complete = false;
- if (tcp_cumulative_ > 2) {
- uint16_t dns_length = readUint16(tcp_receive_buffer_,
- sizeof(tcp_receive_buffer_));
- complete = ((dns_length + 2) == tcp_cumulative_);
- }
-
- if (!complete) {
- if (debug_) {
- cout << "tcpReceiveHandler(): read not complete, " <<
- "issuing another read" << endl;
- }
-
- // Not complete yet, issue another read.
- tcp_socket_.async_receive(
- asio::buffer(tcp_receive_buffer_ + tcp_cumulative_,
- sizeof(tcp_receive_buffer_) - tcp_cumulative_),
- boost::bind(&RecursiveQueryTest2::tcpReceiveHandler, this, _1, _2));
- return;
- }
-
- // Have received a TCP message. Expected state should be one greater
- // than the last state.
- EXPECT_EQ(static_cast<int>(expected_), static_cast<int>(last_) + 1);
- last_ = expected_;
-
- // Check that question we received is what was expected. Note that we
- // have to ignore the two-byte header in order to parse the message.
- qid_t qid = checkReceivedPacket(tcp_receive_buffer_ + 2, length - 2);
-
- // Return a message back. This is a referral to example.org, which
- // should result in another query over UDP. Note the setting of the
- // QID in the returned message with what was in the received message.
- Message msg(Message::RENDER);
- setCommonMessage(msg, qid);
- setReferralExampleOrg(msg);
-
- // Convert to wire format
- // Use a temporary renderer for the dns wire data (we copy it
- // to the 'real' buffer below)
- MessageRenderer renderer;
- msg.toWire(renderer);
-
- // Expected next state (when checked) is the UDP query to example.org.
- // Also, take this opportunity to clear the accumulated read count in
- // readiness for the next read. (If any - at present, there is only
- // one read in the test, although extensions to this test suite could
- // change that.)
- expected_ = UDP_EXAMPLE_ORG_BAD;
- tcp_cumulative_ = 0;
-
- // Unless we go through a callback loop we cannot simply use
- // async_send() multiple times, so we cannot send the size first
- // followed by the actual data. We copy them to a new buffer
- // first
- tcp_send_buffer_->clear();
- tcp_send_buffer_->writeUint16(renderer.getLength());
- tcp_send_buffer_->writeData(renderer.getData(), renderer.getLength());
- tcp_socket_.async_send(asio::buffer(tcp_send_buffer_->getData(),
- tcp_send_buffer_->getLength()),
- boost::bind(
- &RecursiveQueryTest2::tcpSendHandler, this,
- tcp_send_buffer_->getLength(), _1, _2));
- }
-
- /// \brief Completion Handler for Sending TCP data
- ///
- /// Called when the asynchronous send of data back to the RecursiveQuery
- /// by the TCP "server" in this class has completed. (This send has to
- /// be asynchronous because control needs to return to the caller in order
- /// for the IOService "run()" method to be called to run the handlers.)
- ///
- /// \param expected_length Number of bytes that were expected to have been sent.
- /// \param ec Boost error code, value should be zero.
- /// \param length Number of bytes sent.
- void tcpSendHandler(size_t expected_length = 0,
- asio::error_code ec = asio::error_code(),
- size_t length = 0)
- {
- if (debug_) {
- cout << "tcpSendHandler(): error = " << ec.value() <<
- ", length = " << length <<
- ", (expected length = " << expected_length << ")" << endl;
- }
- EXPECT_EQ(0, ec.value()); // Expect no error
- EXPECT_EQ(expected_length, length); // And that amount sent is as expected
- }
-
- /// \brief Check Received Packet
- ///
- /// Checks the packet received from the RecursiveQuery object to ensure
- /// that the question is what is expected.
- ///
- /// \param data Start of data. This is the start of the received buffer in
- /// the case of UDP data, and an offset into the buffer past the
- /// count field for TCP data.
- /// \param length Length of data.
- /// \return The QID of the message
- qid_t checkReceivedPacket(uint8_t* data, size_t length) {
-
- // Decode the received buffer.
- InputBuffer buffer(data, length);
- Message message(Message::PARSE);
- message.fromWire(buffer);
-
- // Check the packet.
- EXPECT_FALSE(message.getHeaderFlag(Message::HEADERFLAG_QR));
-
- Question question = **(message.beginQuestion());
- EXPECT_TRUE(question == *question_);
-
- return message.getQid();
- }
-};
-
-/// \brief Resolver Callback Object
-///
-/// Holds the success and failure callback methods for the resolver
-class ResolverCallback : public isc::resolve::ResolverInterface::Callback {
-public:
- /// \brief Constructor
- ResolverCallback(IOService& service) :
- service_(service), run_(false), status_(false), debug_(DEBUG_PRINT)
- {}
-
- /// \brief Destructor
- virtual ~ResolverCallback()
- {}
-
- /// \brief Resolver Callback Success
- ///
- /// Called if the resolver detects that the call has succeeded.
- ///
- /// \param response Answer to the question.
- virtual void success(const isc::dns::MessagePtr response) {
- if (debug_) {
- cout << "ResolverCallback::success(): answer received" << endl;
- cout << response->toText() << endl;
- }
-
- // There should be one RR each in the question and answer sections, and
- // two RRs in each of the the authority and additional sections.
- EXPECT_EQ(1, response->getRRCount(Message::SECTION_QUESTION));
- EXPECT_EQ(1, response->getRRCount(Message::SECTION_ANSWER));
- EXPECT_EQ(2, response->getRRCount(Message::SECTION_AUTHORITY));
- EXPECT_EQ(2, response->getRRCount(Message::SECTION_ADDITIONAL));
-
- // Check the answer - that the RRset is there...
- EXPECT_TRUE(response->hasRRset(Message::SECTION_ANSWER,
- RRsetPtr(new RRset(Name("www.example.org."),
- RRClass::IN(),
- RRType::A(),
- RRTTL(300)))));
- const RRsetIterator rrset_i = response->beginSection(Message::SECTION_ANSWER);
-
- // ... get iterator into the Rdata of this RRset and point to first
- // element...
- const RdataIteratorPtr rdata_i = (*rrset_i)->getRdataIterator();
- rdata_i->first();
-
- // ... and check it is what we expect.
- EXPECT_EQ(string(WWW_EXAMPLE_ORG), rdata_i->getCurrent().toText());
-
- // Flag completion
- run_ = true;
- status_ = true;
-
- service_.stop(); // Cause run() to exit.
- }
-
- /// \brief Resolver Failure Completion
- ///
- /// Called if the resolver detects that the resolution has failed.
- virtual void failure() {
- if (debug_) {
- cout << "ResolverCallback::success(): resolution failure" << endl;
- }
- FAIL() << "Resolver reported completion failure";
-
- // Flag completion
- run_ = true;
- status_ = false;
-
- service_.stop(); // Cause run() to exit.
- }
-
- /// \brief Return status of "run" flag
- bool getRun() const {
- return (run_);
- }
-
- /// \brief Return "status" flag
- bool getStatus() const {
- return (status_);
- }
-
-private:
- IOService& service_; ///< Service handling the run queue
- bool run_; ///< Set true when completion handler run
- bool status_; ///< Set true for success, false on error
- bool debug_; ///< Debug flag
-};
-
-// Sets up the UDP and TCP "servers", then tries a resolution.
-
-TEST_F(RecursiveQueryTest2, Resolve) {
- // Set up the UDP server and issue the first read. The endpoint from which
- // the query is sent is put in udp_endpoint_ when the read completes, which
- // is referenced in the callback as the place to which the response is sent.
- udp_socket_.set_option(socket_base::reuse_address(true));
- udp_socket_.bind(udp::endpoint(address::from_string(TEST_ADDRESS), TEST_PORT));
- udp_socket_.async_receive_from(asio::buffer(udp_receive_buffer_,
- sizeof(udp_receive_buffer_)),
- udp_remote_,
- boost::bind(&RecursiveQueryTest2::udpReceiveHandler,
- this, _1, _2));
-
- // Set up the TCP server and issue the accept. Acceptance will cause the
- // read to be issued.
- tcp::acceptor acceptor(service_.get_io_service(),
- tcp::endpoint(tcp::v4(), TEST_PORT));
- acceptor.async_accept(tcp_socket_,
- boost::bind(&RecursiveQueryTest2::tcpAcceptHandler,
- this, _1, 0));
-
- // Set up the RecursiveQuery object. We will also test that it correctly records
- // RTT times by setting up a RTT recorder object as well.
- std::vector<std::pair<std::string, uint16_t> > upstream; // Empty
- std::vector<std::pair<std::string, uint16_t> > upstream_root; // Empty
- RecursiveQuery query(dns_service_, *nsas_, cache_,
- upstream, upstream_root);
- query.setTestServer(TEST_ADDRESS, TEST_PORT);
-
- boost::shared_ptr<RttRecorder> recorder(new RttRecorder());
- query.setRttRecorder(recorder);
-
- // Set up callback to receive notification that the query has completed.
- isc::resolve::ResolverInterface::CallbackPtr
- resolver_callback(new ResolverCallback(service_));
-
- // Kick off the resolution process. We expect the first question to go to
- // "root".
- expected_ = UDP_ROOT;
- running_query_ = query.resolve(question_, resolver_callback);
- service_.run();
-
- // Check what ran. (We have to cast the callback to ResolverCallback as we
- // lost the information on the derived class when we used a
- // ResolverInterface::CallbackPtr to store a pointer to it.)
- ResolverCallback* rc = static_cast<ResolverCallback*>(resolver_callback.get());
- EXPECT_TRUE(rc->getRun());
- EXPECT_TRUE(rc->getStatus());
-
- // Finally, check that all the RTTs were "reasonable" (defined here as
- // being below 2 seconds). This is an explicit check to test that the
- // variables in the RTT calculation are at least being initialized; if they
- // weren't, we would expect some absurdly high answers.
- vector<uint32_t> rtt = recorder->getRtt();
- EXPECT_GT(rtt.size(), 0);
- for (vector<uint32_t>::size_type i = 0; i < rtt.size(); ++i) {
- EXPECT_LT(rtt[i], 2000);
- }
-}
-
-} // namespace asiodns
-} // namespace isc
+++ /dev/null
-// 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 <algorithm>
-#include <cstdlib>
-#include <iomanip>
-#include <iostream>
-#include <string>
-#include <vector>
-
-#include <gtest/gtest.h>
-#include <boost/bind.hpp>
-
-#include <asio.hpp>
-
-#include <util/buffer.h>
-#include <util/io_utilities.h>
-
-#include <dns/question.h>
-#include <dns/message.h>
-#include <dns/messagerenderer.h>
-#include <dns/opcode.h>
-#include <dns/name.h>
-#include <dns/rcode.h>
-#include <dns/rrtype.h>
-#include <dns/rrset.h>
-#include <dns/rrttl.h>
-#include <dns/rdata.h>
-
-#include <util/io_utilities.h>
-#include <asiodns/dns_service.h>
-#include <asiodns/io_fetch.h>
-#include <asiolink/io_address.h>
-#include <asiolink/io_endpoint.h>
-#include <asiolink/io_service.h>
-#include <resolve/recursive_query.h>
-#include <resolve/resolver_interface.h>
-
-using namespace asio;
-using namespace asio::ip;
-using namespace isc::asiolink;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::util;
-using namespace isc::resolve;
-using namespace std;
-
-/// RecursiveQuery Test - 3
-///
-/// The second part of the RecursiveQuery unit tests, this attempts to get the
-/// RecursiveQuery object to follow a set of EDNS-induced errors, causing the
-/// resolver to follow the fallback logic.
-///
-/// - Send EDNS question over UDP - get FORMERR
-/// - Send EDNS question over TCP - get FORMERR
-/// - Send non-EDNS question over UDP - get RESPONSE
-///
-/// By using the "test_server_" element of RecursiveQuery, all queries are
-/// directed to one or other of the "servers" in the RecursiveQueryTest3 class.
-
-namespace {
-const char* const TEST_ADDRESS3 = "127.0.0.1"; ///< Servers are on this address
-const uint16_t TEST_PORT3 = 5303; ///< ... and this port
-const size_t BUFFER_SIZE = 1024; ///< For all buffers
-
-const char* const DUMMY_ADDR3 = "1.2.3.4"; ///< address to return as A
-} // end anonymous namespace
-
-namespace isc {
-namespace asiodns {
-
-class MockResolver3 : public isc::resolve::ResolverInterface {
-public:
- virtual void resolve(const QuestionPtr& question,
- const ResolverInterface::CallbackPtr& callback) {
- }
-
- virtual ~MockResolver3() {}
-};
-
-
-
-/// \brief Test fixture for the RecursiveQuery Test
-class RecursiveQueryTest3 : public virtual ::testing::Test
-{
-public:
-
- /// \brief Status of query
- ///
- /// Set before the query and then by each "server" when responding.
- enum QueryStatus {
- NONE = 0, ///< Default
- EDNS_UDP = 1, ///< EDNS query over UDP
- NON_EDNS_UDP = 2, ///< Non-EDNS query over UDP
- COMPLETE = 6 ///< Query is complete
- };
-
- // Common stuff
- IOService service_; ///< Service to run everything
- DNSService dns_service_; ///< Resolver is part of "server"
- QuestionPtr question_; ///< What to ask
- QueryStatus last_; ///< What was the last state
- QueryStatus expected_; ///< Expected next state
- OutputBufferPtr question_buffer_; ///< Question we expect to receive
- boost::shared_ptr<MockResolver3> resolver_; ///< Mock resolver
- isc::nsas::NameserverAddressStore* nsas_; ///< Nameserver address store
- isc::cache::ResolverCache cache_; ///< Resolver cache
-
- // Data for TCP Server
- size_t tcp_cumulative_; ///< Cumulative TCP data received
- tcp::endpoint tcp_endpoint_; ///< Endpoint for TCP receives
- size_t tcp_length_; ///< Expected length value
- uint8_t tcp_receive_buffer_[BUFFER_SIZE]; ///< Receive buffer for TCP I/O
- OutputBufferPtr tcp_send_buffer_; ///< Send buffer for TCP I/O
- tcp::socket tcp_socket_; ///< Socket used by TCP server
-
- /// Data for UDP
- udp::endpoint udp_remote_; ///< Endpoint for UDP receives
- size_t udp_length_; ///< Expected length value
- uint8_t udp_receive_buffer_[BUFFER_SIZE]; ///< Receive buffer for UDP I/O
- OutputBufferPtr udp_send_buffer_; ///< Send buffer for UDP I/O
- udp::socket udp_socket_; ///< Socket used by UDP server
-
- /// Some of the tests cause an 'active' running query to be created, but
- /// don't complete the framework that makes that query delete itself.
- /// This member can be used to store it so that it is deleted automatically
- /// when the test is finished.
- AbstractRunningQuery* running_query_;
-
- /// \brief Constructor
- RecursiveQueryTest3() :
- service_(),
- dns_service_(service_, NULL, NULL),
- question_(new Question(Name("ednsfallback"),
- RRClass::IN(), RRType::A())),
- last_(NONE),
- expected_(NONE),
- question_buffer_(new OutputBuffer(BUFFER_SIZE)),
- resolver_(new MockResolver3()),
- nsas_(new isc::nsas::NameserverAddressStore(resolver_)),
- tcp_cumulative_(0),
- tcp_endpoint_(asio::ip::address::from_string(TEST_ADDRESS3),
- TEST_PORT3),
- tcp_length_(0),
- tcp_receive_buffer_(),
- tcp_send_buffer_(new OutputBuffer(BUFFER_SIZE)),
- tcp_socket_(service_.get_io_service()),
- udp_remote_(),
- udp_length_(0),
- udp_receive_buffer_(),
- udp_send_buffer_(new OutputBuffer(BUFFER_SIZE)),
- udp_socket_(service_.get_io_service(), udp::v4()),
- running_query_(NULL)
- {
- }
-
- ~RecursiveQueryTest3() {
- delete nsas_;
- // It would delete itself, but after the io_service_, which could
- // segfailt in case there were unhandled requests
- resolver_.reset();
- // In a similar note, we wait until the resolver has been cleaned up
- // until deleting and active test running_query_
- delete running_query_;
- }
-
- /// \brief Set Common Message Bits
- ///
- /// Sets up the common bits of a response message returned by the handlers.
- ///
- /// \param message Message buffer in RENDER mode.
- /// \param qid QID to set the message to
- void setCommonMessage(isc::dns::Message& message, uint16_t qid) {
- message.setQid(qid);
- message.setHeaderFlag(Message::HEADERFLAG_QR);
- message.setOpcode(Opcode::QUERY());
- message.setHeaderFlag(Message::HEADERFLAG_AA);
- message.addQuestion(*question_);
- }
-
- /// \brief Set FORMERR answer
- ///
- /// \param message Message to update with FORMERR status
- void setFORMERR(isc::dns::Message& message) {
- message.setRcode(Rcode::FORMERR());
- }
-
- /// \brief Set Answer
- ///
- /// \param message Message to update with FORMERR status
- void setAnswer(isc::dns::Message& message) {
- // Give a response
- RRsetPtr answer(new RRset(Name("ednsfallback."), RRClass::IN(),
- RRType::A(), RRTTL(300)));
- answer->addRdata(createRdata(RRType::A(), RRClass::IN(), DUMMY_ADDR3));
- message.addRRset(Message::SECTION_ANSWER, answer);
- message.setRcode(Rcode::NOERROR());
- }
-
- /// \brief UDP Receive Handler
- ///
- /// This is invoked when a message is received over UDP from the
- /// RecursiveQuery object under test. It formats an answer and sends it
- /// asynchronously, with the UdpSendHandler method being specified as the
- /// completion handler.
- ///
- /// \param ec ASIO error code, completion code of asynchronous I/O issued
- /// by the "server" to receive data.
- /// \param length Amount of data received.
- void udpReceiveHandler(asio::error_code ec = asio::error_code(),
- size_t length = 0) {
- // Expected state should be one greater than the last state.
- EXPECT_EQ(static_cast<int>(expected_), static_cast<int>(last_) + 1);
- last_ = expected_;
-
- Message query(Message::PARSE);
-
- // The QID in the incoming data is random so set it to 0 for the
- // data comparison check. (It is set to 0 in the buffer containing
- // the expected data.)
- // And check that question we received is what was expected.
- checkReceivedPacket(udp_receive_buffer_, length, query);
-
- // The message returned depends on what state we are in. Set up
- // common stuff first: bits not mentioned are set to 0.
- Message message(Message::RENDER);
- setCommonMessage(message, query.getQid());
-
- // Set up state-dependent bits:
- switch (expected_) {
- case EDNS_UDP:
- EXPECT_TRUE(query.getEDNS());
- // Return FORMERROR
- setFORMERR(message);
- expected_ = NON_EDNS_UDP;
- break;
-
- case NON_EDNS_UDP:
- EXPECT_FALSE(query.getEDNS());
- // Return the answer to the question.
- setAnswer(message);
- expected_ = COMPLETE;
- break;
-
- default:
- FAIL() << "UdpReceiveHandler called with unknown state";
- }
-
- // Convert to wire format
- udp_send_buffer_->clear();
- MessageRenderer renderer;
- renderer.setBuffer(udp_send_buffer_.get());
- message.toWire(renderer);
- renderer.setBuffer(NULL);
-
- // Return a message back to the IOFetch object (after setting the
- // expected length of data for the check in the send handler).
- udp_length_ = udp_send_buffer_->getLength();
- udp_socket_.async_send_to(asio::buffer(udp_send_buffer_->getData(),
- udp_send_buffer_->getLength()),
- udp_remote_,
- boost::bind(&RecursiveQueryTest3::udpSendHandler,
- this, _1, _2));
- }
-
- /// \brief UDP Send Handler
- ///
- /// Called when a send operation of the UDP server (i.e. a response
- /// being sent to the RecursiveQuery) has completed, this re-issues
- /// a read call.
- ///
- /// \param ec Completion error code of the send.
- /// \param length Actual number of bytes sent.
- void udpSendHandler(asio::error_code ec = asio::error_code(),
- size_t length = 0) {
- // Check send was OK
- EXPECT_EQ(0, ec.value());
- EXPECT_EQ(udp_length_, length);
-
- // Reissue the receive call to await the next message.
- udp_socket_.async_receive_from(
- asio::buffer(udp_receive_buffer_, sizeof(udp_receive_buffer_)),
- udp_remote_,
- boost::bind(&RecursiveQueryTest3::udpReceiveHandler,
- this, _1, _2));
- }
-
- /// \brief Completion Handler for Accepting TCP Data
- ///
- /// Called when the remote system connects to the "TCP server". It issues
- /// an asynchronous read on the socket to read data.
- ///
- /// \param socket Socket on which data will be received
- /// \param ec Boost error code, value should be zero.
- void tcpAcceptHandler(asio::error_code ec = asio::error_code(),
- size_t length = 0) {
- // Expect that the accept completed without a problem.
- EXPECT_EQ(0, ec.value());
-
- // Initiate a read on the socket, indicating that nothing has yet been
- // received.
- tcp_cumulative_ = 0;
- tcp_socket_.async_receive(
- asio::buffer(tcp_receive_buffer_, sizeof(tcp_receive_buffer_)),
- boost::bind(&RecursiveQueryTest3::tcpReceiveHandler, this, _1, _2));
- }
-
- /// \brief Completion Handler for Receiving TCP Data
- ///
- /// Reads data from the RecursiveQuery object and loops, reissuing reads,
- /// until all the message has been read. It then returns an appropriate
- /// response.
- ///
- /// \param socket Socket to use to send the answer
- /// \param ec ASIO error code, completion code of asynchronous I/O issued
- /// by the "server" to receive data.
- /// \param length Amount of data received.
- void tcpReceiveHandler(asio::error_code ec = asio::error_code(),
- size_t length = 0) {
- // Expect that the receive completed without a problem.
- EXPECT_EQ(0, ec.value());
-
- // Have we received all the data? We know this by checking if the two-
- // byte length count in the message is equal to the data received.
- tcp_cumulative_ += length;
- bool complete = false;
- if (tcp_cumulative_ > 2) {
- uint16_t dns_length = readUint16(tcp_receive_buffer_,
- sizeof(tcp_receive_buffer_));
- complete = ((dns_length + 2) == tcp_cumulative_);
- }
-
- if (!complete) {
- // Not complete yet, issue another read.
- tcp_socket_.async_receive(
- asio::buffer(tcp_receive_buffer_ + tcp_cumulative_,
- sizeof(tcp_receive_buffer_) - tcp_cumulative_),
- boost::bind(&RecursiveQueryTest3::tcpReceiveHandler,
- this, _1, _2));
- return;
- }
-
- // Have received a TCP message. Expected state should be one greater
- // than the last state.
- EXPECT_EQ(static_cast<int>(expected_), static_cast<int>(last_) + 1);
- last_ = expected_;
-
- Message query(Message::PARSE);
-
- // Check that question we received is what was expected. Note that we
- // have to ignore the two-byte header in order to parse the message.
- checkReceivedPacket(tcp_receive_buffer_ + 2, length - 2, query);
-
- // Return a message back. This is a referral to example.org, which
- // should result in another query over UDP. Note the setting of the
- // QID in the returned message with what was in the received message.
- Message message(Message::RENDER);
- setCommonMessage(message, query.getQid());
-
- // Set up state-dependent bits:
- switch (expected_) {
- default:
- FAIL() << "TcpReceiveHandler called with unknown state";
- }
-
-
- // Convert to wire format
- // Use a temporary buffer for the dns wire data (we copy it
- // to the 'real' buffer below)
- MessageRenderer renderer;
- message.toWire(renderer);
-
- // Also, take this opportunity to clear the accumulated read count in
- // readiness for the next read. (If any - at present, there is only
- // one read in the test, although extensions to this test suite could
- // change that.)
- tcp_cumulative_ = 0;
-
- // Unless we go through a callback loop we cannot simply use
- // async_send() multiple times, so we cannot send the size first
- // followed by the actual data. We copy them to a new buffer
- // first
- tcp_send_buffer_->clear();
- tcp_send_buffer_->writeUint16(renderer.getLength());
- tcp_send_buffer_->writeData(renderer.getData(), renderer.getLength());
- tcp_socket_.async_send(asio::buffer(tcp_send_buffer_->getData(),
- tcp_send_buffer_->getLength()),
- boost::bind(
- &RecursiveQueryTest3::tcpSendHandler,
- this,
- tcp_send_buffer_->getLength(), _1, _2));
- }
-
- /// \brief Completion Handler for Sending TCP data
- ///
- /// Called when the asynchronous send of data back to the RecursiveQuery
- /// by the TCP "server" in this class has completed. (This send has to
- /// be asynchronous because control needs to return to the caller in order
- /// for the IOService "run()" method to be called to run the handlers.)
- ///
- /// \param expected_length Number of bytes that were expected to have been
- /// sent.
- /// \param ec Boost error code, value should be zero.
- /// \param length Number of bytes sent.
- void tcpSendHandler(size_t expected_length = 0,
- asio::error_code ec = asio::error_code(),
- size_t length = 0)
- {
- EXPECT_EQ(0, ec.value()); // Expect no error
- EXPECT_EQ(expected_length, length); // And that amount sent is as
- // expected
- }
-
- /// \brief Check Received Packet
- ///
- /// Checks the packet received from the RecursiveQuery object to ensure
- /// that the question is what is expected.
- ///
- /// \param data Start of data. This is the start of the received buffer in
- /// the case of UDP data, and an offset into the buffer past the
- /// count field for TCP data.
- /// \param length Length of data.
- /// \return The QID of the message
- void checkReceivedPacket(uint8_t* data, size_t length, Message& message) {
-
- // Decode the received buffer.
- InputBuffer buffer(data, length);
- message.fromWire(buffer);
-
- // Check the packet.
- EXPECT_FALSE(message.getHeaderFlag(Message::HEADERFLAG_QR));
-
- Question question = **(message.beginQuestion());
- EXPECT_TRUE(question == *question_);
- }
-};
-
-/// \brief Resolver Callback Object
-///
-/// Holds the success and failure callback methods for the resolver
-class ResolverCallback3 : public isc::resolve::ResolverInterface::Callback {
-public:
- /// \brief Constructor
- ResolverCallback3(IOService& service) :
- service_(service), run_(false), status_(false)
- {}
-
- /// \brief Destructor
- virtual ~ResolverCallback3()
- {}
-
- /// \brief Resolver Callback Success
- ///
- /// Called if the resolver detects that the call has succeeded.
- ///
- /// \param response Answer to the question.
- virtual void success(const isc::dns::MessagePtr response) {
- // There should be one RR each in the question and answer sections,
- // and two RRs in each of the the authority and additional sections.
- EXPECT_EQ(1, response->getRRCount(Message::SECTION_QUESTION));
- EXPECT_EQ(1, response->getRRCount(Message::SECTION_ANSWER));
-
- // Check the answer - that the RRset is there...
- EXPECT_TRUE(response->hasRRset(Message::SECTION_ANSWER,
- RRsetPtr(new RRset(Name("ednsfallback."),
- RRClass::IN(),
- RRType::A(),
- RRTTL(300)))));
- const RRsetIterator rrset_i = response->beginSection(Message::SECTION_ANSWER);
-
- // ... get iterator into the Rdata of this RRset and point to first
- // element...
- const RdataIteratorPtr rdata_i = (*rrset_i)->getRdataIterator();
- rdata_i->first();
-
- // ... and check it is what we expect.
- EXPECT_EQ(string(DUMMY_ADDR3), rdata_i->getCurrent().toText());
-
- // Flag completion
- run_ = true;
- status_ = true;
-
- service_.stop(); // Cause run() to exit.
- }
-
- /// \brief Resolver Failure Completion
- ///
- /// Called if the resolver detects that the resolution has failed.
- virtual void failure() {
- FAIL() << "Resolver reported completion failure";
-
- // Flag completion
- run_ = true;
- status_ = false;
-
- service_.stop(); // Cause run() to exit.
- }
-
- /// \brief Return status of "run" flag
- bool getRun() const {
- return (run_);
- }
-
- /// \brief Return "status" flag
- bool getStatus() const {
- return (status_);
- }
-
-private:
- IOService& service_; ///< Service handling the run queue
- bool run_; ///< Set true when completion handler run
- bool status_; ///< Set true for success, false on error
-};
-
-// Sets up the UDP and TCP "servers", then tries a resolution.
-
-TEST_F(RecursiveQueryTest3, Resolve) {
- // Set up the UDP server and issue the first read. The endpoint from which
- // the query is sent is put in udp_endpoint_ when the read completes, which
- // is referenced in the callback as the place to which the response is
- // sent.
- udp_socket_.set_option(socket_base::reuse_address(true));
- udp_socket_.bind(udp::endpoint(address::from_string(TEST_ADDRESS3),
- TEST_PORT3));
- udp_socket_.async_receive_from(asio::buffer(udp_receive_buffer_,
- sizeof(udp_receive_buffer_)),
- udp_remote_,
- boost::bind(&RecursiveQueryTest3::udpReceiveHandler,
- this, _1, _2));
-
- // Set up the TCP server and issue the accept. Acceptance will cause the
- // read to be issued.
- tcp::acceptor acceptor(service_.get_io_service(),
- tcp::endpoint(tcp::v4(), TEST_PORT3));
- acceptor.async_accept(tcp_socket_,
- boost::bind(&RecursiveQueryTest3::tcpAcceptHandler,
- this, _1, 0));
-
- // Set up the RecursiveQuery object. We will also test that it correctly
- // records RTT times by setting up a RTT recorder object as well.
- std::vector<std::pair<std::string, uint16_t> > upstream; // Empty
- std::vector<std::pair<std::string, uint16_t> > upstream_root; // Empty
- RecursiveQuery query(dns_service_, *nsas_, cache_,
- upstream, upstream_root);
- query.setTestServer(TEST_ADDRESS3, TEST_PORT3);
-
- boost::shared_ptr<RttRecorder> recorder(new RttRecorder());
- query.setRttRecorder(recorder);
-
- // Set up callback to receive notification that the query has completed.
- isc::resolve::ResolverInterface::CallbackPtr
- resolver_callback(new ResolverCallback3(service_));
-
- // Kick off the resolution process.
- expected_ = EDNS_UDP;
- running_query_ = query.resolve(question_, resolver_callback);
- service_.run();
-
- // Check what ran. (We have to cast the callback to ResolverCallback3 as we
- // lost the information on the derived class when we used a
- // ResolverInterface::CallbackPtr to store a pointer to it.)
- ResolverCallback3* rc
- = static_cast<ResolverCallback3*>(resolver_callback.get());
- EXPECT_TRUE(rc->getRun());
- EXPECT_TRUE(rc->getStatus());
-
- // Finally, check that all the RTTs were "reasonable" (defined here as
- // being below 2 seconds). This is an explicit check to test that the
- // variables in the RTT calculation are at least being initialized; if they
- // weren't, we would expect some absurdly high answers.
- vector<uint32_t> rtt = recorder->getRtt();
- EXPECT_GT(rtt.size(), 0);
- for (int i = 0; i < rtt.size(); ++i) {
- EXPECT_LT(rtt[i], 2000);
- }
-}
-
-} // namespace asiodns
-} // namespace isc
+++ /dev/null
-// Copyright (C) 2010 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 <iostream>
-#include <gtest/gtest.h>
-
-#include <dns/message.h>
-#include <dns/question.h>
-#include <dns/opcode.h>
-#include <dns/rcode.h>
-#include <dns/rrttl.h>
-#include <dns/rdata.h>
-#include <resolve/resolve.h>
-
-using namespace isc::dns;
-
-namespace {
-
-class ResolveHelperFunctionsTest : public ::testing::Test {
-public:
- ResolveHelperFunctionsTest() :
- message_a_(new Message(Message::RENDER)),
- message_b_(new Message(Message::RENDER)),
- question_(new Question(Name("www.example.com"), RRClass::IN(), RRType::A()))
- {
- createMessageA();
- createMessageB();
- };
-
- void createMessageA() {
- message_a_->setOpcode(Opcode::QUERY());
- message_a_->setRcode(Rcode::NOERROR());
- message_a_->addQuestion(question_);
- }
-
- void createMessageB() {
- message_b_->setOpcode(Opcode::QUERY());
- message_b_->setRcode(Rcode::NOERROR());
- message_b_->addQuestion(question_);
-
- // We could reuse the same rrset in the different sections,
- // but to be sure, we create separate ones
- RRsetPtr answer_rrset(new RRset(Name("www.example.com"),
- RRClass::IN(), RRType::TXT(),
- RRTTL(3600)));
- answer_rrset->addRdata(rdata::createRdata(RRType::TXT(),
- RRClass::IN(),
- "Answer"));
- message_b_->addRRset(Message::SECTION_ANSWER, answer_rrset);
-
- RRsetPtr auth_rrset(new RRset(Name("www.example.com"),
- RRClass::IN(), RRType::TXT(),
- RRTTL(3600)));
- auth_rrset->addRdata(rdata::createRdata(RRType::TXT(),
- RRClass::IN(),
- "Authority"));
- auth_rrset->addRdata(rdata::createRdata(RRType::TXT(),
- RRClass::IN(),
- "Rdata"));
- message_b_->addRRset(Message::SECTION_AUTHORITY, auth_rrset);
-
- RRsetPtr add_rrset(new RRset(Name("www.example.com"),
- RRClass::IN(), RRType::TXT(),
- RRTTL(3600)));
- add_rrset->addRdata(rdata::createRdata(RRType::TXT(),
- RRClass::IN(),
- "Additional"));
- add_rrset->addRdata(rdata::createRdata(RRType::TXT(),
- RRClass::IN(),
- "Rdata"));
- add_rrset->addRdata(rdata::createRdata(RRType::TXT(),
- RRClass::IN(),
- "fields."));
- message_b_->addRRset(Message::SECTION_ADDITIONAL, add_rrset);
- };
-
- MessagePtr message_a_;
- MessagePtr message_b_;
- QuestionPtr question_;
-};
-
-TEST_F(ResolveHelperFunctionsTest, makeErrorMessageEmptyMessage) {
- ASSERT_EQ(Rcode::NOERROR(), message_a_->getRcode());
- ASSERT_EQ(1, message_a_->getRRCount(Message::SECTION_QUESTION));
- ASSERT_EQ(0, message_a_->getRRCount(Message::SECTION_ANSWER));
- ASSERT_EQ(0, message_a_->getRRCount(Message::SECTION_AUTHORITY));
- ASSERT_EQ(0, message_a_->getRRCount(Message::SECTION_ADDITIONAL));
-
- isc::resolve::makeErrorMessage(message_a_, Rcode::SERVFAIL());
- EXPECT_EQ(Rcode::SERVFAIL(), message_a_->getRcode());
- EXPECT_EQ(1, message_a_->getRRCount(Message::SECTION_QUESTION));
- EXPECT_EQ(0, message_a_->getRRCount(Message::SECTION_ANSWER));
- EXPECT_EQ(0, message_a_->getRRCount(Message::SECTION_AUTHORITY));
- EXPECT_EQ(0, message_a_->getRRCount(Message::SECTION_ADDITIONAL));
-}
-
-TEST_F(ResolveHelperFunctionsTest, makeErrorMessageNonEmptyMessage) {
-
- ASSERT_EQ(Rcode::NOERROR(), message_b_->getRcode());
- ASSERT_EQ(1, message_b_->getRRCount(Message::SECTION_QUESTION));
- ASSERT_EQ(1, message_b_->getRRCount(Message::SECTION_ANSWER));
- ASSERT_EQ(2, message_b_->getRRCount(Message::SECTION_AUTHORITY));
- ASSERT_EQ(3, message_b_->getRRCount(Message::SECTION_ADDITIONAL));
-
- isc::resolve::makeErrorMessage(message_b_, Rcode::FORMERR());
- EXPECT_EQ(Rcode::FORMERR(), message_b_->getRcode());
- EXPECT_EQ(1, message_b_->getRRCount(Message::SECTION_QUESTION));
- EXPECT_EQ(0, message_b_->getRRCount(Message::SECTION_ANSWER));
- EXPECT_EQ(0, message_b_->getRRCount(Message::SECTION_AUTHORITY));
- EXPECT_EQ(0, message_b_->getRRCount(Message::SECTION_ADDITIONAL));
-}
-
-void
-compareSections(const Message& message_a, const Message& message_b,
- Message::Section section)
-{
- RRsetIterator rrs_a = message_a.beginSection(section);
- RRsetIterator rrs_b = message_b.beginSection(section);
- while (rrs_a != message_a.endSection(section) &&
- rrs_b != message_b.endSection(section)
- ) {
- EXPECT_EQ(*rrs_a, *rrs_b);
- ++rrs_a;
- ++rrs_b;
- }
- // can't use EXPECT_EQ here, no eqHelper for endsection comparison
- EXPECT_TRUE(rrs_a == message_a.endSection(section));
- EXPECT_TRUE(rrs_b == message_b.endSection(section));
-}
-
-TEST_F(ResolveHelperFunctionsTest, initResponseMessage) {
- Message response_parse(Message::PARSE);
- EXPECT_THROW(isc::resolve::initResponseMessage(*message_a_,
- response_parse),
- isc::dns::InvalidMessageOperation);
- EXPECT_THROW(isc::resolve::initResponseMessage(*question_,
- response_parse),
- isc::dns::InvalidMessageOperation);
-
- Message response1(Message::RENDER);
- isc::resolve::initResponseMessage(*message_a_, response1);
- ASSERT_EQ(message_a_->getOpcode(), response1.getOpcode());
- ASSERT_EQ(message_a_->getQid(), response1.getQid());
- isc::dns::QuestionIterator qi = response1.beginQuestion();
- ASSERT_EQ(*question_, **qi);
- ASSERT_TRUE(++qi == response1.endQuestion());
-
- Message response2(Message::RENDER);
- isc::resolve::initResponseMessage(*question_, response2);
- ASSERT_EQ(Opcode::QUERY(), response2.getOpcode());
- ASSERT_EQ(0, response2.getQid());
- qi = response2.beginQuestion();
- ASSERT_EQ(*question_, **qi);
- ASSERT_TRUE(++qi == response2.endQuestion());
-}
-
-TEST_F(ResolveHelperFunctionsTest, copyAnswerMessage) {
- message_b_->setRcode(Rcode::NXDOMAIN());
-
- ASSERT_NE(message_b_->getRcode(), message_a_->getRcode());
- ASSERT_NE(message_b_->getRRCount(Message::SECTION_ANSWER),
- message_a_->getRRCount(Message::SECTION_ANSWER));
- ASSERT_NE(message_b_->getRRCount(Message::SECTION_AUTHORITY),
- message_a_->getRRCount(Message::SECTION_AUTHORITY));
- ASSERT_NE(message_b_->getRRCount(Message::SECTION_ADDITIONAL),
- message_a_->getRRCount(Message::SECTION_ADDITIONAL));
- ASSERT_EQ(0, message_a_->getRRCount(Message::SECTION_ANSWER));
- ASSERT_EQ(0, message_a_->getRRCount(Message::SECTION_AUTHORITY));
- ASSERT_EQ(0, message_a_->getRRCount(Message::SECTION_ADDITIONAL));
-
- isc::resolve::copyResponseMessage(*message_b_, message_a_);
-
- EXPECT_EQ(message_b_->getRcode(), message_a_->getRcode());
- ASSERT_EQ(message_b_->getRRCount(Message::SECTION_ANSWER),
- message_a_->getRRCount(Message::SECTION_ANSWER));
- ASSERT_EQ(message_b_->getRRCount(Message::SECTION_AUTHORITY),
- message_a_->getRRCount(Message::SECTION_AUTHORITY));
- ASSERT_EQ(message_b_->getRRCount(Message::SECTION_ADDITIONAL),
- message_a_->getRRCount(Message::SECTION_ADDITIONAL));
-
-
- compareSections(*message_a_, *message_b_, Message::SECTION_ANSWER);
- compareSections(*message_a_, *message_b_, Message::SECTION_AUTHORITY);
- compareSections(*message_a_, *message_b_, Message::SECTION_ADDITIONAL);
-}
-
-} // Anonymous namespace
+++ /dev/null
-// Copyright (C) 2010 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 <gtest/gtest.h>
-#include <asiodns/dns_server.h>
-#include <resolve/resolver_callback.h>
-
-using namespace isc::resolve;
-
-// Dummy subclass for DNSServer*
-// We want to check if resume is called
-// Since the server will get cloned(), we want the clones to share
-// our bools for whether resume got called and with what value
-class DummyServer : public isc::asiodns::DNSServer {
-public:
- DummyServer(DummyServer* orig) {
- resume_called_ = orig->getResumeCalled();
- resume_value_ = orig->getResumeValue();
- }
- DummyServer(bool* resume_called, bool* resume_value) :
- resume_called_(resume_called), resume_value_(resume_value)
- {}
-
- bool* getResumeCalled() { return resume_called_; }
- bool* getResumeValue() { return resume_value_; }
-
- DNSServer* clone() {
- DummyServer* n = new DummyServer(this);
- return n;
- }
-
- void resume(bool value) {
- *resume_called_ = true;
- *resume_value_ = value;
- }
-
-private:
- bool* resume_called_;
- bool* resume_value_;
-};
-
-class ResolverCallbackServerTest : public ::testing::Test {
-public:
- ResolverCallbackServerTest() : resume_called_(false),
- resume_value_(false) {
- server_ = new DummyServer(&resume_called_, &resume_value_);
- callback_ = new ResolverCallbackServer(server_);
- };
-
- ~ResolverCallbackServerTest() {
- delete callback_;
- delete server_;
- }
-
- DummyServer* getServer() { return server_; }
- ResolverCallbackServer* getCallback() { return callback_; }
- bool getResumeCalled() { return resume_called_; }
- bool getResumeValue() { return resume_value_; }
-
-private:
- DummyServer* server_;
- ResolverCallbackServer* callback_;
- bool resume_called_;
- bool resume_value_;
-};
-
-TEST_F(ResolverCallbackServerTest, testSuccess) {
- EXPECT_FALSE(getResumeCalled());
- getCallback()->success(isc::dns::MessagePtr());
- EXPECT_TRUE(getResumeCalled());
- EXPECT_TRUE(getResumeValue());
-}
-
-TEST_F(ResolverCallbackServerTest, testFailure) {
- EXPECT_FALSE(getResumeCalled());
- getCallback()->failure();
- EXPECT_TRUE(getResumeCalled());
- EXPECT_FALSE(getResumeValue());
-}
+++ /dev/null
-// Copyright (C) 2010 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 <iostream>
-#include <gtest/gtest.h>
-
-#include <dns/tests/unittest_util.h>
-
-#include <resolve/response_classifier.h>
-
-#include <dns/name.h>
-#include <dns/opcode.h>
-#include <dns/question.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rcode.h>
-#include <dns/rrclass.h>
-#include <dns/rrset.h>
-#include <dns/rrtype.h>
-#include <dns/rrttl.h>
-
-using namespace std;
-using namespace isc::dns;
-using namespace rdata;
-using namespace isc::dns::rdata::generic;
-using namespace isc::dns::rdata::in;
-using namespace isc::resolve;
-
-
-namespace {
-class ResponseClassifierTest : public ::testing::Test {
-public:
- /// \brief Constructor
- ///
- /// The naming convention is:
- ///
- /// <category>_<class>_<type>_<name>
- ///
- /// <category> is "qu" (question), "rrs" (rrset),
- /// <qclass> is self-explanatory
- /// <qtype> is self-explanatory
- /// <name> is the first part of the domain name (all expected to be in
- /// example.com)
- ///
- /// Message variables
- ///
- /// msg_<qtype> Where <qtype> is the type of query. These are only used
- /// in the early tests where simple messages are required.
-
- ResponseClassifierTest() :
- msg_a(Message::RENDER),
- msg_any(Message::RENDER),
- qu_ch_a_www(Name("www.example.com"), RRClass::CH(), RRType::A()),
- qu_in_any_www(Name("www.example.com"), RRClass::IN(), RRType::ANY()),
- qu_in_a_www2(Name("www2.example.com"), RRClass::IN(), RRType::A()),
- qu_in_a_www(Name("www.example.com"), RRClass::IN(), RRType::A()),
- qu_in_cname_www1(Name("www1.example.com"), RRClass::IN(), RRType::A()),
- qu_in_ns_(Name("example.com"), RRClass::IN(), RRType::NS()),
- qu_in_txt_www(Name("www.example.com"), RRClass::IN(), RRType::TXT()),
- rrs_hs_txt_www(new RRset(Name("www.example.com"), RRClass::HS(),
- RRType::TXT(), RRTTL(300))),
- rrs_in_a_mail(new RRset(Name("mail.example.com"), RRClass::IN(),
- RRType::A(), RRTTL(300))),
- rrs_in_a_www(new RRset(Name("www.example.com"), RRClass::IN(),
- RRType::A(), RRTTL(300))),
- rrs_in_cname_www1(new RRset(Name("www1.example.com"), RRClass::IN(),
- RRType::CNAME(), RRTTL(300))),
- rrs_in_cname_www2(new RRset(Name("www2.example.com"), RRClass::IN(),
- RRType::CNAME(), RRTTL(300))),
- rrs_in_ns_(new RRset(Name("example.com"), RRClass::IN(),
- RRType::NS(), RRTTL(300))),
- rrs_in_soa_(new RRset(Name("example.com"), RRClass::IN(),
- RRType::SOA(), RRTTL(300))),
- rrs_in_txt_www(new RRset(Name("www.example.com"), RRClass::IN(),
- RRType::TXT(), RRTTL(300))),
- cname_target("."),
- cname_count(0)
- {
- // Set up the message to indicate a successful response to the question
- // "www.example.com A", but don't add in any response sections.
- msg_a.setHeaderFlag(Message::HEADERFLAG_QR);
- msg_a.setOpcode(Opcode::QUERY());
- msg_a.setRcode(Rcode::NOERROR());
- msg_a.addQuestion(qu_in_a_www);
-
- // ditto for the query "www.example.com ANY"
- msg_any.setHeaderFlag(Message::HEADERFLAG_QR);
- msg_any.setOpcode(Opcode::QUERY());
- msg_any.setRcode(Rcode::NOERROR());
- msg_any.addQuestion(qu_in_any_www);
-
- // The next set of assignments set up the following zone records
- //
- // example.com NS ns0.isc.org
- // NS ns0.example.org
- //
- // www.example.com A 1.2.3.4
- // TXT "An example text string"
- //
- // mail.example.com A 4.5.6.7
- //
- // www1.example.com CNAME www.example.com
- //
- // www2.example.com CNAME www1.example.com
-
- // Set up an imaginary NS RRset for an authority section
- rrs_in_ns_->addRdata(ConstRdataPtr(new NS(Name("ns0.isc.org"))));
- rrs_in_ns_->addRdata(ConstRdataPtr(new NS(Name("ns0.example.org"))));
-
- // And an imaginary SOA
- rrs_in_soa_->addRdata(ConstRdataPtr(new SOA(Name("ns0.example.org"), Name("root.example.org"), 1, 2, 3, 4, 5)));
-
- // Set up the records for the www host
- rrs_in_a_www->addRdata(ConstRdataPtr(new A("1.2.3.4")));
- rrs_in_txt_www->addRdata(ConstRdataPtr(
- new TXT("An example text string")));
-
- // ... for the mail host
- rrs_in_a_mail->addRdata(ConstRdataPtr(new A("5.6.7.8")));
-
- // ... the CNAME records
- rrs_in_cname_www1->addRdata(ConstRdataPtr(
- new CNAME("www.example.com.")));
- rrs_in_cname_www2->addRdata(ConstRdataPtr(
- new CNAME("www1.example.com.")));
- }
-
- Message msg_a; // Pointer to message in RENDER state
- Message msg_any; // Pointer to message in RENDER state
- Question qu_ch_a_www; // www.example.com CH A
- Question qu_in_any_www; // www.example.com IN ANY
- Question qu_in_a_www2; // www.example.com IN ANY
- Question qu_in_a_www; // www.example.com IN A
- Question qu_in_cname_www1; // www1.example.com IN CNAME
- Question qu_in_ns_; // example.com IN NS
- Question qu_in_txt_www; // www.example.com IN TXT
- RRsetPtr rrs_hs_txt_www; // www.example.com HS TXT
- RRsetPtr rrs_in_a_mail; // mail.example.com IN A
- RRsetPtr rrs_in_a_www; // www.example.com IN A
- RRsetPtr rrs_in_cname_www1; // www1.example.com IN CNAME
- RRsetPtr rrs_in_cname_www2; // www2.example.com IN CNAME
- RRsetPtr rrs_in_ns_; // example.com IN NS
- RRsetPtr rrs_in_soa_; // example.com IN SOA
- RRsetPtr rrs_in_txt_www; // www.example.com IN TXT
- Name cname_target; // Used in response classifier to
- // store the target of a possible
- // CNAME chain
- unsigned int cname_count; // Used to count cnames in a chain
-};
-
-// Test that the error() function categorises the codes correctly.
-
-TEST_F(ResponseClassifierTest, StatusCodes) {
- EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::ANSWER));
- EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::ANSWERCNAME));
- EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::CNAME));
- EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::NXDOMAIN));
- EXPECT_FALSE(ResponseClassifier::error(ResponseClassifier::REFERRAL));
-
- EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::EMPTY));
- EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::EXTRADATA));
- EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::INVNAMCLASS));
- EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::INVTYPE));
- EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::MISMATQUEST));
- EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::MULTICLASS));
- EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::NOTONEQUEST));
- EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::NOTRESPONSE));
- EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::NOTSINGLE));
- EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::OPCODE));
- EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::RCODE));
- EXPECT_TRUE(ResponseClassifier::error(ResponseClassifier::TRUNCATED));
-}
-
-// Test that the system will reject a message which is a query.
-
-TEST_F(ResponseClassifierTest, Query) {
-
- // Set up message to indicate a query (QR flag = 0, one question). By
- // default the opcode will be 0 (query)
- msg_a.setHeaderFlag(Message::HEADERFLAG_QR, false);
-
- // Should be rejected as it is a query, not a response
- EXPECT_EQ(ResponseClassifier::NOTRESPONSE,
- ResponseClassifier::classify(qu_in_a_www, msg_a,
- cname_target, cname_count));
-}
-
-// Check that we get an OPCODE error on all but QUERY opcodes.
-
-TEST_F(ResponseClassifierTest, Opcode) {
-
- uint8_t query = static_cast<uint8_t>(Opcode::QUERY().getCode());
-
- for (uint8_t i = 0; i < (1 << 4); ++i) {
- msg_a.setOpcode(Opcode(i));
- if (i == query) {
- EXPECT_NE(ResponseClassifier::OPCODE,
- ResponseClassifier::classify(qu_in_a_www, msg_a,
- cname_target, cname_count));
- } else {
- EXPECT_EQ(ResponseClassifier::OPCODE,
- ResponseClassifier::classify(qu_in_a_www, msg_a,
- cname_target, cname_count));
- }
- }
-}
-
-// Test that the system will reject a response with anything other than one
-// question.
-
-TEST_F(ResponseClassifierTest, MultipleQuestions) {
-
- // Create a message object for this test that has no question section.
- Message message(Message::RENDER);
- message.setHeaderFlag(Message::HEADERFLAG_QR);
- message.setOpcode(Opcode::QUERY());
- message.setRcode(Rcode::NOERROR());
-
- // Zero questions
- EXPECT_EQ(ResponseClassifier::NOTONEQUEST,
- ResponseClassifier::classify(qu_in_a_www, message,
- cname_target, cname_count));
-
- // One question
- message.addQuestion(qu_in_a_www);
- EXPECT_NE(ResponseClassifier::NOTONEQUEST,
- ResponseClassifier::classify(qu_in_a_www, message,
- cname_target, cname_count));
-
- // Two questions
- message.addQuestion(qu_in_ns_);
- EXPECT_EQ(ResponseClassifier::NOTONEQUEST,
- ResponseClassifier::classify(qu_in_a_www, message,
- cname_target, cname_count));
-
- // And finish the check with three questions
- message.addQuestion(qu_in_txt_www);
- EXPECT_EQ(ResponseClassifier::NOTONEQUEST,
- ResponseClassifier::classify(qu_in_a_www, message,
- cname_target, cname_count));
-}
-
-// Test that the question in the question section in the message response
-// is equal to the question supplied.
-
-TEST_F(ResponseClassifierTest, SameQuestion) {
-
- EXPECT_EQ(ResponseClassifier::MISMATQUEST,
- ResponseClassifier::classify(qu_in_ns_, msg_a,
- cname_target, cname_count));
- EXPECT_NE(ResponseClassifier::MISMATQUEST,
- ResponseClassifier::classify(qu_in_a_www, msg_a,
- cname_target, cname_count));
-}
-
-// Should get an NXDOMAIN response only on an NXDOMAIN RCODE.
-
-TEST_F(ResponseClassifierTest, NXDOMAIN) {
-
- uint16_t nxdomain = static_cast<uint16_t>(Rcode::NXDOMAIN().getCode());
-
- for (uint8_t i = 0; i < (1 << 4); ++i) {
- msg_a.setRcode(Rcode(i));
- if (i == nxdomain) {
- EXPECT_EQ(ResponseClassifier::NXDOMAIN,
- ResponseClassifier::classify(qu_in_a_www, msg_a,
- cname_target, cname_count));
- } else {
- EXPECT_NE(ResponseClassifier::NXDOMAIN,
- ResponseClassifier::classify(qu_in_a_www, msg_a,
- cname_target, cname_count));
- }
- }
-}
-
-// Check that we get an RCODE error on all but NXDOMAIN and NOERROR responses.
-
-TEST_F(ResponseClassifierTest, RCODE) {
-
- uint16_t nxdomain = static_cast<uint16_t>(Rcode::NXDOMAIN().getCode());
- uint16_t noerror = static_cast<uint16_t>(Rcode::NOERROR().getCode());
-
- for (uint8_t i = 0; i < (1 << 4); ++i) {
- msg_a.setRcode(Rcode(i));
- if ((i == nxdomain) || (i == noerror)) {
- EXPECT_NE(ResponseClassifier::RCODE,
- ResponseClassifier::classify(qu_in_a_www, msg_a,
- cname_target, cname_count));
- } else {
- EXPECT_EQ(ResponseClassifier::RCODE,
- ResponseClassifier::classify(qu_in_a_www, msg_a,
- cname_target, cname_count));
- }
- }
-}
-
-// Test that the code will detect a truncated message. Even if nothing else
-// is wrong, we'll want to retry the query if we receive a truncated code.
-// However, we give the option to the user of the code aws to whether they
-// want to take into account the truncated bit.
-
-TEST_F(ResponseClassifierTest, Truncated) {
-
- // Don't expect the truncated code whatever option we ask for if the TC
- // bit is not set.
- msg_a.setHeaderFlag(Message::HEADERFLAG_TC, false);
- EXPECT_NE(ResponseClassifier::TRUNCATED,
- ResponseClassifier::classify(qu_in_a_www, msg_a, cname_target,
- cname_count, true));
- EXPECT_NE(ResponseClassifier::TRUNCATED,
- ResponseClassifier::classify(qu_in_a_www, msg_a, cname_target,
- cname_count, false));
-
- // Expect the truncated code if the TC bit is set, only if we don't ignore
- // it.
- msg_a.setHeaderFlag(Message::HEADERFLAG_TC, true);
- EXPECT_NE(ResponseClassifier::TRUNCATED,
- ResponseClassifier::classify(qu_in_a_www, msg_a, cname_target,
- cname_count, true));
- EXPECT_EQ(ResponseClassifier::TRUNCATED,
- ResponseClassifier::classify(qu_in_a_www, msg_a, cname_target,
- cname_count, false));
-}
-
-// Check for an empty packet (i.e. no error, but with the answer and additional
-// sections empty).
-
-TEST_F(ResponseClassifierTest, Empty) {
-
- EXPECT_EQ(ResponseClassifier::EMPTY,
- ResponseClassifier::classify(qu_in_a_www, msg_a, cname_target,
- cname_count));
-}
-
-// Anything where we have an empty answer section but something in the
-// authority section is a referral (if the status is NOERROR).
-
-TEST_F(ResponseClassifierTest, EmptyAnswerReferral) {
-
- msg_a.addRRset(Message::SECTION_AUTHORITY, rrs_in_ns_);
- EXPECT_EQ(ResponseClassifier::REFERRAL,
- ResponseClassifier::classify(qu_in_a_www, msg_a, cname_target,
- cname_count));
-
-}
-
-// Test if we get a NOERROR answer that contains neither an actual
-// answer nor a delegation
-TEST_F(ResponseClassifierTest, NoErrorNoData) {
-
- msg_a.addRRset(Message::SECTION_AUTHORITY, rrs_in_soa_);
- EXPECT_EQ(ResponseClassifier::NXRRSET,
- ResponseClassifier::classify(qu_in_a_www, msg_a, cname_target,
- cname_count));
-
-}
-
-// Check the case where we have a simple answer in the answer section. This
-// occurs when the QNAME/QTYPE/QCLASS matches one of the RRsets in the
-// answer section - expect when the QTYPE is ANY, in which case the match
-// must be on the QNAME/QCLASS alone.
-
-TEST_F(ResponseClassifierTest, SingleAnswer) {
-
- // Check a question that matches the answer
- msg_a.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
- EXPECT_EQ(ResponseClassifier::ANSWER,
- ResponseClassifier::classify(qu_in_a_www, msg_a, cname_target,
- cname_count));
-
- // Check an ANY question that matches the answer
- msg_any.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
- EXPECT_EQ(ResponseClassifier::ANSWER,
- ResponseClassifier::classify(qu_in_any_www, msg_any, cname_target,
- cname_count));
-
- // Check a CNAME response that matches the QNAME.
- Message message_a(Message::RENDER);
- message_a.setHeaderFlag(Message::HEADERFLAG_QR);
- message_a.setOpcode(Opcode::QUERY());
- message_a.setRcode(Rcode::NOERROR());
- message_a.addQuestion(qu_in_cname_www1);
- message_a.addRRset(Message::SECTION_ANSWER, rrs_in_cname_www1);
- EXPECT_EQ(ResponseClassifier::CNAME,
- ResponseClassifier::classify(qu_in_cname_www1, message_a,
- cname_target, cname_count));
-
- // Check if the answer QNAME does not match the question
- // Q: www.example.com IN A
- // A: mail.example.com IN A
- Message message_b(Message::RENDER);
- message_b.setHeaderFlag(Message::HEADERFLAG_QR);
- message_b.setOpcode(Opcode::QUERY());
- message_b.setRcode(Rcode::NOERROR());
- message_b.addQuestion(qu_in_a_www);
- message_b.addRRset(Message::SECTION_ANSWER, rrs_in_a_mail);
- EXPECT_EQ(ResponseClassifier::INVNAMCLASS,
- ResponseClassifier::classify(qu_in_a_www, message_b,
- cname_target, cname_count));
-
- // Check if the answer class does not match the question
- // Q: www.example.com CH A
- // A: www.example.com IN A
- Message message_c(Message::RENDER);
- message_c.setHeaderFlag(Message::HEADERFLAG_QR);
- message_c.setOpcode(Opcode::QUERY());
- message_c.setRcode(Rcode::NOERROR());
- message_c.addQuestion(qu_ch_a_www);
- message_c.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
- EXPECT_EQ(ResponseClassifier::INVNAMCLASS,
- ResponseClassifier::classify(qu_ch_a_www, message_c,
- cname_target, cname_count));
-
- // Check if the answer type does not match the question
- // Q: www.example.com IN A
- // A: www.example.com IN TXT
- Message message_d(Message::RENDER);
- message_d.setHeaderFlag(Message::HEADERFLAG_QR);
- message_d.setOpcode(Opcode::QUERY());
- message_d.setRcode(Rcode::NOERROR());
- message_d.addQuestion(qu_in_a_www);
- message_d.addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
- EXPECT_EQ(ResponseClassifier::INVTYPE,
- ResponseClassifier::classify(qu_in_a_www, message_d,
- cname_target, cname_count));
-}
-
-// Check what happens if we have multiple RRsets in the answer.
-
-TEST_F(ResponseClassifierTest, MultipleAnswerRRsets) {
-
- // All the same QNAME but different types is only valid on an ANY query.
- Message message_a(Message::RENDER);
- message_a.setHeaderFlag(Message::HEADERFLAG_QR);
- message_a.setOpcode(Opcode::QUERY());
- message_a.setRcode(Rcode::NOERROR());
- message_a.addQuestion(qu_in_any_www);
- message_a.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
- message_a.addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
- EXPECT_EQ(ResponseClassifier::ANSWER,
- ResponseClassifier::classify(qu_in_any_www, message_a,
- cname_target, cname_count));
-
- // On another type of query, it results in an EXTRADATA error
- Message message_b(Message::RENDER);
- message_b.setHeaderFlag(Message::HEADERFLAG_QR);
- message_b.setOpcode(Opcode::QUERY());
- message_b.setRcode(Rcode::NOERROR());
- message_b.addQuestion(qu_in_a_www);
- message_b.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
- message_b.addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
- EXPECT_EQ(ResponseClassifier::EXTRADATA,
- ResponseClassifier::classify(qu_in_a_www, message_b,
- cname_target, cname_count));
-
- // Same QNAME on an ANY query is not valid with mixed classes
- Message message_c(Message::RENDER);
- message_c.setHeaderFlag(Message::HEADERFLAG_QR);
- message_c.setOpcode(Opcode::QUERY());
- message_c.setRcode(Rcode::NOERROR());
- message_c.addQuestion(qu_in_any_www);
- message_c.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
- message_c.addRRset(Message::SECTION_ANSWER, rrs_hs_txt_www);
- EXPECT_EQ(ResponseClassifier::MULTICLASS,
- ResponseClassifier::classify(qu_in_any_www, message_c,
- cname_target, cname_count));
-
- // Mixed QNAME is not valid unless QNAME requested is a CNAME.
- Message message_d(Message::RENDER);
- message_d.setHeaderFlag(Message::HEADERFLAG_QR);
- message_d.setOpcode(Opcode::QUERY());
- message_d.setRcode(Rcode::NOERROR());
- message_d.addQuestion(qu_in_a_www);
- message_d.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
- message_d.addRRset(Message::SECTION_ANSWER, rrs_in_a_mail);
- EXPECT_EQ(ResponseClassifier::EXTRADATA,
- ResponseClassifier::classify(qu_in_a_www, message_d,
- cname_target, cname_count));
-
- // Mixed QNAME is not valid when the query is an ANY.
- Message message_e(Message::RENDER);
- message_e.setHeaderFlag(Message::HEADERFLAG_QR);
- message_e.setOpcode(Opcode::QUERY());
- message_e.setRcode(Rcode::NOERROR());
- message_e.addQuestion(qu_in_any_www);
- message_e.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
- message_e.addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
- message_e.addRRset(Message::SECTION_ANSWER, rrs_in_a_mail);
- EXPECT_EQ(ResponseClassifier::EXTRADATA,
- ResponseClassifier::classify(qu_in_any_www, message_e,
- cname_target, cname_count));
-}
-
-// CNAME chain is CNAME if it terminates in a CNAME, answer if it
-// does not, and error if there are RRs left over.
-TEST_F(ResponseClassifierTest, CNAMEChain) {
-
- // Answer contains a single CNAME
- Message message_a(Message::RENDER);
- message_a.setHeaderFlag(Message::HEADERFLAG_QR);
- message_a.setOpcode(Opcode::QUERY());
- message_a.setRcode(Rcode::NOERROR());
- message_a.addQuestion(qu_in_a_www2);
- message_a.addRRset(Message::SECTION_ANSWER, rrs_in_cname_www2);
- EXPECT_EQ(ResponseClassifier::CNAME,
- ResponseClassifier::classify(qu_in_a_www2, message_a,
- cname_target, cname_count));
-
- // Add a CNAME for www1, and it should still return a CNAME
- message_a.addRRset(Message::SECTION_ANSWER, rrs_in_cname_www1);
- EXPECT_EQ(ResponseClassifier::CNAME,
- ResponseClassifier::classify(qu_in_a_www2, message_a,
- cname_target, cname_count));
-
- // Add the A record for www and it should be an answer
- message_a.addRRset(Message::SECTION_ANSWER, rrs_in_a_www);
- EXPECT_EQ(ResponseClassifier::ANSWERCNAME,
- ResponseClassifier::classify(qu_in_a_www2, message_a,
- cname_target, cname_count));
-
- // Adding an unrelated TXT record should result in EXTRADATA
- message_a.addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
- EXPECT_EQ(ResponseClassifier::EXTRADATA,
- ResponseClassifier::classify(qu_in_a_www2, message_a,
- cname_target, cname_count));
-
- // Recreate the chain, but this time end with a TXT RR and not the A
- // record. This should return INVTYPE.
- Message message_b(Message::RENDER);
- message_b.setHeaderFlag(Message::HEADERFLAG_QR);
- message_b.setOpcode(Opcode::QUERY());
- message_b.setRcode(Rcode::NOERROR());
- message_b.addQuestion(qu_in_a_www2);
- message_b.addRRset(Message::SECTION_ANSWER, rrs_in_cname_www2);
- message_b.addRRset(Message::SECTION_ANSWER, rrs_in_cname_www1);
- message_b.addRRset(Message::SECTION_ANSWER, rrs_in_txt_www);
-
- EXPECT_EQ(ResponseClassifier::INVTYPE,
- ResponseClassifier::classify(qu_in_a_www2, message_b,
- cname_target, cname_count));
-}
-
-} // Anonymous namespace
+++ /dev/null
-// Copyright (C) 2009 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 <gtest/gtest.h>
-#include <util/unittests/run_all.h>
-
-#include <dns/tests/unittest_util.h>
-#include <log/logger_support.h>
-
-int
-main(int argc, char* argv[]) {
- ::testing::InitGoogleTest(&argc, argv);
- isc::log::initLogger();
-
- return (isc::util::unittests::run_all());
-}
#include <cc/data.h>
#include <cc/session.h>
-#include <xfr/xfrout_client.h>
+//#include <xfr/xfrout_client.h>
#include <asiodns/asiodns.h>
bool done_;
};
+// Commenting out this class as it is not used anyware. The removal of this class
+// allows for removal of the lib/xfr.
+
// Mock Xfrout client
-class MockXfroutClient : public isc::xfr::AbstractXfroutClient {
+/*class MockXfroutClient : public isc::xfr::AbstractXfroutClient {
public:
MockXfroutClient() :
is_connected_(false), connect_ok_(true), send_ok_(true),
bool connect_ok_;
bool send_ok_;
bool disconnect_ok_;
-};
+ }; */
} // end of testutils
} // end of isc
+++ /dev/null
-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)
-AM_CXXFLAGS += -Wno-unused-parameter
-# see src/lib/cc/Makefile.am
-
-CLEANFILES = *.gcno *.gcda
-
-lib_LTLIBRARIES = libb10-xfr.la
-libb10_xfr_la_SOURCES = xfrout_client.h xfrout_client.cc
-libb10_xfr_la_LIBADD = $(top_builddir)/src/lib/util/io/libb10-util-io.la
+++ /dev/null
-/run_unittests
+++ /dev/null
-AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CXXFLAGS = $(B10_CXXFLAGS)
-
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
-CLEANFILES = *.gcno *.gcda
-
-TESTS_ENVIRONMENT = \
- $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
-
-TESTS =
-if HAVE_GTEST
-TESTS += run_unittests
-run_unittests_SOURCES = run_unittests.cc client_test.cc
-
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-
-run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libb10-xfr.la
-run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
-endif
-
-noinst_PROGRAMS = $(TESTS)
+++ /dev/null
-// 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 <gtest/gtest.h>
-
-#include <sys/un.h>
-#include <string>
-
-#include <xfr/xfrout_client.h>
-
-using namespace std;
-using namespace isc::xfr;
-
-namespace {
-
-TEST(ClientTest, connectFile) {
- // File path is too long
- struct sockaddr_un s; // can't be const; some compiler complains
- EXPECT_THROW(XfroutClient(string(sizeof(s.sun_path), 'x')).connect(),
- XfroutError);
-
- // File doesn't exist (we assume the file "no_such_file" doesn't exist)
- EXPECT_THROW(XfroutClient("no_such_file").connect(), XfroutError);
-}
-
-}
+++ /dev/null
-// 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 <gtest/gtest.h>
-#include <log/logger_support.h>
-#include <util/unittests/run_all.h>
-
-int
-main(int argc, char* argv[]) {
- ::testing::InitGoogleTest(&argc, argv);
- isc::log::initLogger();
- return (isc::util::unittests::run_all());
-}
+++ /dev/null
-// Copyright (C) 2010 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 <cstdlib>
-#include <cstring>
-#include <iostream>
-
-// for some IPC/network system calls in asio/detail/pipe_select_interrupter.hpp
-#include <unistd.h>
-#include <asio.hpp>
-
-#include <util/io/fd_share.h>
-#include <xfr/xfrout_client.h>
-
-using namespace std;
-using namespace isc::util::io;
-using asio::local::stream_protocol;
-
-namespace isc {
-namespace xfr {
-
-struct XfroutClientImpl {
- XfroutClientImpl(const string& file);
- const std::string file_path_;
- asio::io_service io_service_;
- // The socket used to communicate with the xfrout server.
- stream_protocol::socket socket_;
-};
-
-XfroutClientImpl::XfroutClientImpl(const string& file) :
- file_path_(file), socket_(io_service_)
-{}
-
-XfroutClient::XfroutClient(const string& file) :
- impl_(new XfroutClientImpl(file))
-{}
-
-XfroutClient::~XfroutClient() {
- delete impl_;
-}
-
-void
-XfroutClient::connect() {
- try {
- impl_->socket_.connect(stream_protocol::endpoint(impl_->file_path_));
- } catch (const asio::system_error& err) {
- isc_throw(XfroutError, "socket connect failed for " <<
- impl_->file_path_ << ": " << err.what());
- }
-}
-
-void
-XfroutClient::disconnect() {
- asio::error_code err;
- impl_->socket_.close(err);
- if (err) {
- isc_throw(XfroutError, "close socket failed: " << err.message());
- }
-}
-
-int
-XfroutClient::sendXfroutRequestInfo(const int tcp_sock,
- const void* const msg_data,
- const uint16_t msg_len)
-{
- if (send_fd(impl_->socket_.native(), tcp_sock) < 0) {
- isc_throw(XfroutError,
- "Failed to send the socket file descriptor "
- "to xfrout module");
- }
-
- // TODO: this shouldn't be blocking send, even though it's unlikely to
- // block.
- // Converting the 16-bit word to network byte order.
-
- // Splitting msg_len below performs something called a 'narrowing
- // conversion' (conversion of uint16_t to uint8_t). C++0x (and GCC
- // 4.7) requires explicit casting when a narrowing conversion is
- // performed. For reference, see 8.5.4/6 of n3225.
- const uint8_t lenbuf[2] = { static_cast<uint8_t>(msg_len >> 8),
- static_cast<uint8_t>(msg_len & 0xff) };
- if (send(impl_->socket_.native(), lenbuf, sizeof(lenbuf), 0) !=
- sizeof(lenbuf)) {
- isc_throw(XfroutError,
- "failed to send XFR request length to xfrout module");
- }
- if (send(impl_->socket_.native(), msg_data, msg_len, 0) != msg_len) {
- isc_throw(XfroutError,
- "failed to send XFR request data to xfrout module");
- }
-
- return (0);
-}
-
-} // End for xfr
-} // End for isc
-
+++ /dev/null
-// Copyright (C) 2010 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.
-
-#ifndef XFROUT_CLIENT_H
-#define XFROUT_CLIENT_H
-
-#include <stdint.h>
-
-#include <string>
-
-#include <exceptions/exceptions.h>
-
-namespace isc {
-namespace xfr {
-
-struct XfroutClientImpl;
-
-class XfroutError: public Exception {
-public:
- XfroutError(const char *file, size_t line, const char *what):
- isc::Exception(file, line, what) {}
-};
-
-/// \brief The AbstractXfroutClient class is an abstract base class that
-/// defines the interfaces of XfroutClient.
-///
-/// The intended primary usage of abstraction is to allow tests for the
-/// user class of XfroutClient without requiring actual communication.
-class AbstractXfroutClient {
- ///
- /// \name Constructors, Assignment Operator and Destructor.
- ///
- /// Note: The copy constructor and the assignment operator are
- /// intentionally defined as private to make it explicit that this is a
- /// pure base class.
- //@{
-private:
- AbstractXfroutClient(const AbstractXfroutClient& source);
- AbstractXfroutClient& operator=(const AbstractXfroutClient& source);
-protected:
- /// \brief The default constructor.
- ///
- /// This is intentionally defined as \c protected as this base class should
- /// never be instantiated (except as part of a derived class).
- AbstractXfroutClient() {}
-public:
- /// \brief The destructor.
- virtual ~AbstractXfroutClient() {}
- //@}
- virtual void connect() = 0;
- virtual void disconnect() = 0;
- virtual int sendXfroutRequestInfo(int tcp_sock, const void* msg_data,
- uint16_t msg_len) = 0;
-};
-
-class XfroutClient : public AbstractXfroutClient {
-public:
- XfroutClient(const std::string& file);
- ~XfroutClient();
-private:
- // make this class non copyable
- XfroutClient(const XfroutClient& source);
- XfroutClient& operator=(const XfroutClient& source);
-public:
- virtual void connect();
- virtual void disconnect();
- virtual int sendXfroutRequestInfo(int tcp_sock, const void* msg_data,
- uint16_t msg_len);
-private:
- XfroutClientImpl* impl_;
-};
-
-} // End for namespace xfr
-} // End for namespace isc
-
-#endif
-
-// Local Variables:
-// mode: c++
-// End: