From: Marcin Siodelski Date: Thu, 10 Apr 2014 14:10:23 +0000 (+0200) Subject: [3391] Fix distcheck issues. X-Git-Tag: trac2406k_base~4^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b6e72599dab828c5aa2a0cd04e99d7d024060fec;p=thirdparty%2Fkea.git [3391] Fix distcheck issues. --- diff --git a/configure.ac b/configure.ac index 92c35d4466..a8d6745cea 100644 --- a/configure.ac +++ b/configure.ac @@ -1469,7 +1469,6 @@ AC_CONFIG_FILES([compatcheck/Makefile src/lib/dhcpsrv/tests/Makefile src/lib/dhcpsrv/tests/test_libraries.h src/lib/dhcp/tests/Makefile - src/lib/dns/benchmarks/Makefile src/lib/dns/gen-rdatacode.py src/lib/dns/Makefile src/lib/dns/python/Makefile @@ -1668,11 +1667,6 @@ fi cat >> config.report << END -Components: - DHCP: $want_dhcp - DNS: $want_dns - Experimental resolver: $want_experimental_resolver - Features: $enable_features diff --git a/src/lib/asiodns/dns_service.h b/src/lib/asiodns/dns_service.h index 00d7158bef..4ce25d00c7 100644 --- a/src/lib/asiodns/dns_service.h +++ b/src/lib/asiodns/dns_service.h @@ -15,7 +15,9 @@ #ifndef ASIOLINK_DNS_SERVICE_H #define ASIOLINK_DNS_SERVICE_H 1 -#include +// The commented header occurs to be unused so we remove it to get +// rid of dependency on resolver. +// #include #include #include diff --git a/src/lib/resolve/.gitignore b/src/lib/resolve/.gitignore deleted file mode 100644 index 87dc694968..0000000000 --- a/src/lib/resolve/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/resolve_messages.cc -/resolve_messages.h -/s-messages diff --git a/src/lib/resolve/Makefile.am b/src/lib/resolve/Makefile.am deleted file mode 100644 index dde08f2982..0000000000 --- a/src/lib/resolve/Makefile.am +++ /dev/null @@ -1,48 +0,0 @@ -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) diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc deleted file mode 100644 index e0c5a30034..0000000000 --- a/src/lib/resolve/recursive_query.cc +++ /dev/null @@ -1,1225 +0,0 @@ -// 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 - -#include -#include -#include -#include // for some IPC/network system calls -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -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( - 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 >& upstream, - const std::vector >& 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& recorder) { - rtt_recorder_ = recorder; -} - -namespace { -typedef std::pair 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 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 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 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& 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& 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 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 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 diff --git a/src/lib/resolve/recursive_query.h b/src/lib/resolve/recursive_query.h deleted file mode 100644 index 2875441586..0000000000 --- a/src/lib/resolve/recursive_query.h +++ /dev/null @@ -1,229 +0,0 @@ -// 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 -#include -#include -#include -#include - -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 getRtt() const { - return rtt_; - } - -private: - std::vector rtt_; ///< Stored round-trip times -}; - -typedef std::vector > 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 >& - upstream, - const std::vector >& - 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& 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 - /// \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 - /// \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 > > - upstream_; - boost::shared_ptr > > - upstream_root_; - std::pair test_server_; - int query_timeout_; - int client_timeout_; - int lookup_timeout_; - unsigned retries_; - boost::shared_ptr rtt_recorder_; ///< Round-trip time recorder -}; - -} // namespace asiodns -} // namespace isc -#endif // RECURSIVE_QUERY_H diff --git a/src/lib/resolve/resolve.cc b/src/lib/resolve/resolve.cc deleted file mode 100644 index cfd6217cea..0000000000 --- a/src/lib/resolve/resolve.cc +++ /dev/null @@ -1,78 +0,0 @@ -// 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 - -#include -#include - -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 - diff --git a/src/lib/resolve/resolve.h b/src/lib/resolve/resolve.h deleted file mode 100644 index c7ff195e8f..0000000000 --- a/src/lib/resolve/resolve.h +++ /dev/null @@ -1,95 +0,0 @@ -// 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 -#include -#include - -#include - -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_ diff --git a/src/lib/resolve/resolve_log.cc b/src/lib/resolve/resolve_log.cc deleted file mode 100644 index e41d8d21c5..0000000000 --- a/src/lib/resolve/resolve_log.cc +++ /dev/null @@ -1,26 +0,0 @@ -// 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 - -namespace isc { -namespace resolve { - -isc::log::Logger logger("reslib"); // Distinct from "resolver" - -} // namespace resolve -} // namespace isc - diff --git a/src/lib/resolve/resolve_log.h b/src/lib/resolve/resolve_log.h deleted file mode 100644 index 15a7a729b3..0000000000 --- a/src/lib/resolve/resolve_log.h +++ /dev/null @@ -1,53 +0,0 @@ -// 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 -#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 diff --git a/src/lib/resolve/resolve_messages.mes b/src/lib/resolve/resolve_messages.mes deleted file mode 100644 index c89dedb33a..0000000000 --- a/src/lib/resolve/resolve_messages.mes +++ /dev/null @@ -1,229 +0,0 @@ -# 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 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 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 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 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 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 -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 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 -tuple is being sent to a nameserver whose address is given in the message. diff --git a/src/lib/resolve/resolver_callback.cc b/src/lib/resolve/resolver_callback.cc deleted file mode 100644 index c0db55eb60..0000000000 --- a/src/lib/resolve/resolver_callback.cc +++ /dev/null @@ -1,36 +0,0 @@ -// 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 - -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 diff --git a/src/lib/resolve/resolver_callback.h b/src/lib/resolve/resolver_callback.h deleted file mode 100644 index 9a24501f89..0000000000 --- a/src/lib/resolve/resolver_callback.h +++ /dev/null @@ -1,50 +0,0 @@ -// 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 -#include - -#include - -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_ diff --git a/src/lib/resolve/resolver_interface.h b/src/lib/resolve/resolver_interface.h deleted file mode 100644 index 5002c808bb..0000000000 --- a/src/lib/resolve/resolver_interface.h +++ /dev/null @@ -1,98 +0,0 @@ -// 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 - -/// -/// \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 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 diff --git a/src/lib/resolve/response_classifier.cc b/src/lib/resolve/response_classifier.cc deleted file mode 100644 index 27c5cfcb1c..0000000000 --- a/src/lib/resolve/response_classifier.cc +++ /dev/null @@ -1,279 +0,0 @@ -// 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 -#include - -#include -#include -#include -#include -#include - -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 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 answer( - message.beginSection(Message::SECTION_ANSWER), - message.endSection(Message::SECTION_ANSWER) - ); - const vector 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::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::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::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 ansrrset(answer); - vector 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& ansrrset, vector& present, size_t size) -{ - // Search through the vector of RRset pointers until we find one with the - // right QNAME. - for (vector::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 diff --git a/src/lib/resolve/response_classifier.h b/src/lib/resolve/response_classifier.h deleted file mode 100644 index ce99bf0f76..0000000000 --- a/src/lib/resolve/response_classifier.h +++ /dev/null @@ -1,157 +0,0 @@ -// 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 - -#include -#include -#include - -#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& ansrrset, std::vector& present, - size_t size); -}; - -} // namespace resolve -} // namespace isc - -#endif // RESPONSE_CLASSIFIER_H diff --git a/src/lib/resolve/tests/.gitignore b/src/lib/resolve/tests/.gitignore deleted file mode 100644 index d6d1ec87a1..0000000000 --- a/src/lib/resolve/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/run_unittests diff --git a/src/lib/resolve/tests/Makefile.am b/src/lib/resolve/tests/Makefile.am deleted file mode 100644 index 84f603b3e3..0000000000 --- a/src/lib/resolve/tests/Makefile.am +++ /dev/null @@ -1,45 +0,0 @@ -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) diff --git a/src/lib/resolve/tests/recursive_query_unittest.cc b/src/lib/resolve/tests/recursive_query_unittest.cc deleted file mode 100644 index dddfab3e6b..0000000000 --- a/src/lib/resolve/tests/recursive_query_unittest.cc +++ /dev/null @@ -1,1006 +0,0 @@ -// 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 - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -// 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 -#include -#include -#include -#include -#include - -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 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(io_message.getData()), - static_cast(io_message.getData()) + - io_message.getDataSize()); - io_service_.stop(); - } -protected: - IOService io_service_; - scoped_ptr dns_service_; - scoped_ptr nsas_; - isc::cache::ResolverCache cache_; - scoped_ptr callback_; - int callback_protocol_; - int callback_native_; - string callback_address_; - vector callback_data_; - ScopedSocket sock_; - boost::shared_ptr 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 > -singleAddress(const string &address, uint16_t port) { - vector > result; - result.push_back(pair(address, port)); - return (result); -} - -TEST_F(RecursiveQueryTest, recursiveSetupV4) { - setDNSService(true, false); - uint16_t port = boost::lexical_cast(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(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(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(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 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(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 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(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 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(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 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 > 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 > 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 > roots; - roots.push_back(pair("192.0.2.2", 53)); - vector > 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. - -} diff --git a/src/lib/resolve/tests/recursive_query_unittest_2.cc b/src/lib/resolve/tests/recursive_query_unittest_2.cc deleted file mode 100644 index 8c2e9e3ebf..0000000000 --- a/src/lib/resolve/tests/recursive_query_unittest_2.cc +++ /dev/null @@ -1,728 +0,0 @@ -// 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 -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -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 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(expected_), static_cast(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(expected_), static_cast(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 > upstream; // Empty - std::vector > upstream_root; // Empty - RecursiveQuery query(dns_service_, *nsas_, cache_, - upstream, upstream_root); - query.setTestServer(TEST_ADDRESS, TEST_PORT); - - boost::shared_ptr 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(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 rtt = recorder->getRtt(); - EXPECT_GT(rtt.size(), 0); - for (vector::size_type i = 0; i < rtt.size(); ++i) { - EXPECT_LT(rtt[i], 2000); - } -} - -} // namespace asiodns -} // namespace isc diff --git a/src/lib/resolve/tests/recursive_query_unittest_3.cc b/src/lib/resolve/tests/recursive_query_unittest_3.cc deleted file mode 100644 index acf60d6093..0000000000 --- a/src/lib/resolve/tests/recursive_query_unittest_3.cc +++ /dev/null @@ -1,590 +0,0 @@ -// 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 -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -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 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(expected_), static_cast(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(expected_), static_cast(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 > upstream; // Empty - std::vector > upstream_root; // Empty - RecursiveQuery query(dns_service_, *nsas_, cache_, - upstream, upstream_root); - query.setTestServer(TEST_ADDRESS3, TEST_PORT3); - - boost::shared_ptr 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(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 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 diff --git a/src/lib/resolve/tests/resolve_unittest.cc b/src/lib/resolve/tests/resolve_unittest.cc deleted file mode 100644 index 85d264dd12..0000000000 --- a/src/lib/resolve/tests/resolve_unittest.cc +++ /dev/null @@ -1,198 +0,0 @@ -// 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 -#include - -#include -#include -#include -#include -#include -#include -#include - -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 diff --git a/src/lib/resolve/tests/resolver_callback_unittest.cc b/src/lib/resolve/tests/resolver_callback_unittest.cc deleted file mode 100644 index e94f13d56b..0000000000 --- a/src/lib/resolve/tests/resolver_callback_unittest.cc +++ /dev/null @@ -1,90 +0,0 @@ -// 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 -#include -#include - -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()); -} diff --git a/src/lib/resolve/tests/response_classifier_unittest.cc b/src/lib/resolve/tests/response_classifier_unittest.cc deleted file mode 100644 index 30aeabbb69..0000000000 --- a/src/lib/resolve/tests/response_classifier_unittest.cc +++ /dev/null @@ -1,554 +0,0 @@ -// 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 -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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: - /// - /// ___ - /// - /// is "qu" (question), "rrs" (rrset), - /// is self-explanatory - /// is self-explanatory - /// is the first part of the domain name (all expected to be in - /// example.com) - /// - /// Message variables - /// - /// msg_ Where 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(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(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(Rcode::NXDOMAIN().getCode()); - uint16_t noerror = static_cast(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 diff --git a/src/lib/resolve/tests/run_unittests.cc b/src/lib/resolve/tests/run_unittests.cc deleted file mode 100644 index fe8124ea72..0000000000 --- a/src/lib/resolve/tests/run_unittests.cc +++ /dev/null @@ -1,27 +0,0 @@ -// 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 -#include - -#include -#include - -int -main(int argc, char* argv[]) { - ::testing::InitGoogleTest(&argc, argv); - isc::log::initLogger(); - - return (isc::util::unittests::run_all()); -} diff --git a/src/lib/testutils/mockups.h b/src/lib/testutils/mockups.h index 00c2dcd7d5..1881f78f0c 100644 --- a/src/lib/testutils/mockups.h +++ b/src/lib/testutils/mockups.h @@ -22,7 +22,7 @@ #include #include -#include +//#include #include @@ -173,8 +173,11 @@ private: 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), @@ -215,7 +218,7 @@ private: bool connect_ok_; bool send_ok_; bool disconnect_ok_; -}; + }; */ } // end of testutils } // end of isc diff --git a/src/lib/xfr/Makefile.am b/src/lib/xfr/Makefile.am deleted file mode 100644 index 5e90e9bc28..0000000000 --- a/src/lib/xfr/Makefile.am +++ /dev/null @@ -1,15 +0,0 @@ -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 diff --git a/src/lib/xfr/tests/.gitignore b/src/lib/xfr/tests/.gitignore deleted file mode 100644 index d6d1ec87a1..0000000000 --- a/src/lib/xfr/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/run_unittests diff --git a/src/lib/xfr/tests/Makefile.am b/src/lib/xfr/tests/Makefile.am deleted file mode 100644 index 908abe8edf..0000000000 --- a/src/lib/xfr/tests/Makefile.am +++ /dev/null @@ -1,28 +0,0 @@ -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) diff --git a/src/lib/xfr/tests/client_test.cc b/src/lib/xfr/tests/client_test.cc deleted file mode 100644 index ce783b0158..0000000000 --- a/src/lib/xfr/tests/client_test.cc +++ /dev/null @@ -1,37 +0,0 @@ -// 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 - -#include -#include - -#include - -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); -} - -} diff --git a/src/lib/xfr/tests/run_unittests.cc b/src/lib/xfr/tests/run_unittests.cc deleted file mode 100644 index 8dc59a2a68..0000000000 --- a/src/lib/xfr/tests/run_unittests.cc +++ /dev/null @@ -1,24 +0,0 @@ -// 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 -#include -#include - -int -main(int argc, char* argv[]) { - ::testing::InitGoogleTest(&argc, argv); - isc::log::initLogger(); - return (isc::util::unittests::run_all()); -} diff --git a/src/lib/xfr/xfrout_client.cc b/src/lib/xfr/xfrout_client.cc deleted file mode 100644 index af0c1b51c3..0000000000 --- a/src/lib/xfr/xfrout_client.cc +++ /dev/null @@ -1,108 +0,0 @@ -// 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 -#include -#include - -// for some IPC/network system calls in asio/detail/pipe_select_interrupter.hpp -#include -#include - -#include -#include - -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(msg_len >> 8), - static_cast(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 - diff --git a/src/lib/xfr/xfrout_client.h b/src/lib/xfr/xfrout_client.h deleted file mode 100644 index e691fb1513..0000000000 --- a/src/lib/xfr/xfrout_client.h +++ /dev/null @@ -1,91 +0,0 @@ -// 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 - -#include - -#include - -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: