Commit d82bd9a6 authored by Jelte Jansen's avatar Jelte Jansen
Browse files

[trac495] use the NSAS to ask for a nameserver address

if not in forwarder mode.
parent 19603be4
......@@ -130,7 +130,7 @@ main(int argc, char* argv[]) {
Session* cc_session = NULL;
ModuleCCSession* config_session = NULL;
try {
//try {
string specfile;
if (getenv("B10_FROM_BUILD")) {
specfile = string(getenv("B10_FROM_BUILD")) +
......@@ -151,7 +151,45 @@ main(int argc, char* argv[]) {
isc::cache::ResolverCache cache;
resolver->setCache(cache);
// TODO priming query, remove root from direct
// Fake a priming query result here (TODO2 how to flag non-expiry?)
// propagation to runningquery. And check for forwarder mode?
isc::dns::QuestionPtr root_question(new isc::dns::Question(
isc::dns::Name("."),
isc::dns::RRClass::IN(),
isc::dns::RRType::NS()));
isc::dns::RRsetPtr root_ns_rrset(new isc::dns::RRset(isc::dns::Name("."),
isc::dns::RRClass::IN(),
isc::dns::RRType::NS(),
isc::dns::RRTTL(8888)));
root_ns_rrset->addRdata(isc::dns::rdata::createRdata(isc::dns::RRType::NS(),
isc::dns::RRClass::IN(),
"l.root-servers.net."));
isc::dns::RRsetPtr root_a_rrset(new isc::dns::RRset(isc::dns::Name("l.root-servers.net"),
isc::dns::RRClass::IN(),
isc::dns::RRType::A(),
isc::dns::RRTTL(8888)));
root_a_rrset->addRdata(isc::dns::rdata::createRdata(isc::dns::RRType::A(),
isc::dns::RRClass::IN(),
"199.7.83.42"));
isc::dns::RRsetPtr root_aaaa_rrset(new isc::dns::RRset(isc::dns::Name("l.root-servers.net"),
isc::dns::RRClass::IN(),
isc::dns::RRType::AAAA(),
isc::dns::RRTTL(8888)));
root_aaaa_rrset->addRdata(isc::dns::rdata::createRdata(isc::dns::RRType::AAAA(),
isc::dns::RRClass::IN(),
"2001:500:3::42"));
isc::dns::MessagePtr priming_result(new isc::dns::Message(isc::dns::Message::RENDER));
priming_result->addQuestion(root_question);
priming_result->addRRset(isc::dns::Message::SECTION_ANSWER, root_ns_rrset);
priming_result->addRRset(isc::dns::Message::SECTION_ADDITIONAL, root_a_rrset);
priming_result->addRRset(isc::dns::Message::SECTION_ADDITIONAL, root_aaaa_rrset);
cache.update(*priming_result);
cache.update(root_ns_rrset);
cache.update(root_a_rrset);
cache.update(root_aaaa_rrset);
DNSService dns_service(io_service, checkin, lookup, answer);
resolver->setDNSService(dns_service);
dlog("IOService created.");
......@@ -174,10 +212,10 @@ main(int argc, char* argv[]) {
dlog("Server started.");
io_service.run();
} catch (const std::exception& ex) {
dlog(string("Server failed: ") + ex.what(),true);
ret = 1;
}
//} catch (const std::exception& ex) {
// dlog(string("Server failed: ") + ex.what(),true);
// ret = 1;
//}
delete config_session;
delete cc_session;
......
......@@ -26,6 +26,8 @@
#include <exceptions/exceptions.h>
#include <iostream>
namespace asiolink {
/// \brief The \c IOAddress class represents an IP addresses (version
......
......@@ -30,6 +30,8 @@
#include <resolve/resolve.h>
#include <cache/resolver_cache.h>
#include <nsas/address_request_callback.h>
#include <nsas/nameserver_address.h>
#include <asio.hpp>
#include <asiolink/dns_service.h>
......@@ -76,6 +78,26 @@ typedef std::pair<std::string, uint16_t> addr_t;
* Used by RecursiveQuery::sendQuery.
*/
class RunningQuery : public IOFetch::Callback {
class ResolverNSASCallback : public isc::nsas::AddressRequestCallback {
public:
ResolverNSASCallback(RunningQuery* rq) : rq_(rq) {}
void success(const isc::nsas::NameserverAddress& address) {
rq_->sendTo(address);
}
void unreachable() {
dlog("Nameservers unreachable");
// Drop query or send servfail?
rq_->stop(false);
}
private:
RunningQuery* rq_;
};
private:
// The io service to handle async calls
IOService& io_;
......@@ -144,6 +166,12 @@ private:
// Reference to our cache
isc::cache::ResolverCache& cache_;
// the 'current' nameserver we have a query out to
std::string cur_zone_;
boost::shared_ptr<ResolverNSASCallback> nsas_callback_;
isc::nsas::NameserverAddress current_ns_address;
time_t current_ns_qsent_time;
// perform a single lookup; first we check the cache to see
// if we have a response for our query stored already. if
......@@ -157,13 +185,51 @@ private:
dlog("Message found in cache, returning that");
handleRecursiveAnswer(cached_message);
} else {
cur_zone_ = ".";
send();
}
}
// (re)send the query to the server.
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;
time(&current_ns_qsent_time);
std::cout << "[XX] ORIG SND TIME: " << current_ns_qsent_time << std::endl;
std::cout << "[XX] SENDING: " << question_.toText() << std::endl;
std::cout << "[XX] TO AUTH: " << current_ns_address.getAddress().toText() << std::endl;
IOFetch query(IPPROTO_UDP, io_, question_,
current_ns_address.getAddress(),
53, buffer_, this,
query_timeout_);
++queries_out_;
io_.get_io_service().post(query);
}
void send() {
// If are in forwarder mode, send it to a random
// forwarder. If not, ask the NSAS for an address
const int uc = upstream_->size();
if (uc > 0) {
int serverIndex = rand() % uc;
dlog("Sending upstream query (" + question_.toText() +
") to " + upstream_->at(serverIndex).first);
IOFetch query(IPPROTO_UDP, io_, question_,
upstream_->at(serverIndex).first,
upstream_->at(serverIndex).second, buffer_, this,
query_timeout_);
++queries_out_;
io_.get_io_service().post(query);
} else {
// Ask the NSAS for an address for the current zone,
// the callback will call the actual sendTo()
nsas_.lookup(cur_zone_, question_.getClass(), nsas_callback_);
}
}
// (re)send the query to the server.
void oldsend() {
const int uc = upstream_->size();
const int zs = zone_servers_.size();
buffer_->clear();
......@@ -260,29 +326,21 @@ private:
// Referral. For now we just take the first glue address
// we find and continue with that
zone_servers_.clear();
for (RRsetIterator rrsi = incoming.beginSection(Message::SECTION_ADDITIONAL);
rrsi != incoming.endSection(Message::SECTION_ADDITIONAL) && !found_ns_address;
// auth section should have at least one RRset
// and one of them should be an NS (otherwise
// classifier should have error'd)
// TODO: should we check if it really is subzone?
for (RRsetIterator rrsi = incoming.beginSection(Message::SECTION_AUTHORITY);
rrsi != incoming.endSection(Message::SECTION_AUTHORITY) && !found_ns_address;
rrsi++) {
ConstRRsetPtr rrs = *rrsi;
if (rrs->getType() == RRType::A()) {
// found address
RdataIteratorPtr rdi = rrs->getRdataIterator();
// just use the first for now
if (!rdi->isLast()) {
std::string addr_str = rdi->getCurrent().toText();
// now we have one address, simply
// resend that exact same query
// to that address and yield, when it
// returns, loop again.
// TODO should use NSAS
zone_servers_.push_back(addr_t(addr_str, 53));
found_ns_address = true;
break;
}
if (rrs->getType() == RRType::NS()) {
cur_zone_ = rrs->getName().toText();
found_ns_address = true;
break;
}
}
if (found_ns_address) {
// next resolver round
// we do NOT use doLookup() here, but send() (i.e. we
......@@ -322,7 +380,7 @@ private:
public:
RunningQuery(IOService& io,
const Question &question,
const Question& question,
MessagePtr answer_message,
boost::shared_ptr<AddressVector> upstream,
boost::shared_ptr<AddressVector> upstream_root,
......@@ -348,7 +406,9 @@ public:
done_(false),
answer_sent_(false),
nsas_(nsas),
cache_(cache)
cache_(cache),
nsas_callback_(boost::shared_ptr<ResolverNSASCallback>(
new ResolverNSASCallback(this)))
{
// Setup the timer to stop trying (lookup_timeout)
if (lookup_timeout >= 0) {
......@@ -451,12 +511,25 @@ public:
virtual void operator()(IOFetch::Result result) {
// XXX is this the place for TCP retry?
--queries_out_;
if (!done_ && result != IOFetch::TIME_OUT) {
// we got an answer
// Update the NSAS with the time it took
time_t cur_time;
time(&cur_time);
if (cur_time == current_ns_qsent_time) {
dlog("[XX] well, that was fast. 0 ms? setting it to 1...");
++cur_time;
}
current_ns_address.updateRTT(
static_cast<uint32_t>(cur_time - current_ns_qsent_time));
Message incoming(Message::PARSE);
InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
incoming.fromWire(ibuf);
buffer_->clear();
if (upstream_->size() == 0 &&
incoming.getRcode() == Rcode::NOERROR()) {
done_ = handleRecursiveAnswer(incoming);
......@@ -471,9 +544,11 @@ public:
} else if (!done_ && retries_--) {
// We timed out, but we have some retries, so send again
dlog("Timeout, resending query");
//current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
send();
} else {
// out of retries, give up for now
//current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
stop(false);
}
}
......@@ -499,14 +574,28 @@ RecursiveQuery::resolve(const QuestionPtr& question,
dlog("Message found in cache, returning that");
// TODO: err, should cache set rcode as well?
answer_message->setRcode(Rcode::NOERROR());
std::cout << answer_message->toText();
callback->success(answer_message);
} else {
dlog("Message not found in cache, starting recursive query");
// It will delete itself when it is done
new RunningQuery(io, *question, answer_message, upstream_,
upstream_root_, buffer, callback, query_timeout_,
client_timeout_, lookup_timeout_, retries_,
nsas_, cache_);
// 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) {
dlog("Found single RRset in cache");
answer_message->addRRset(Message::SECTION_ANSWER,
cached_rrset);
answer_message->setRcode(Rcode::NOERROR());
callback->success(answer_message);
} else {
dlog("Message not found in cache, starting recursive query");
// It will delete itself when it is done
new RunningQuery(io, *question, answer_message, upstream_,
upstream_root_, buffer, callback, query_timeout_,
client_timeout_, lookup_timeout_, retries_,
nsas_, cache_);
}
}
}
......@@ -538,11 +627,24 @@ RecursiveQuery::resolve(const Question& question,
answer_message->setRcode(Rcode::NOERROR());
crs->success(answer_message);
} else {
dlog("Message not found in cache, starting recursive query");
// It will delete itself when it is done
new RunningQuery(io, question, answer_message, upstream_, upstream_root_,
buffer, crs, query_timeout_, client_timeout_,
lookup_timeout_, retries_, nsas_, cache_);
// 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) {
dlog("Found single RRset in cache");
answer_message->addRRset(Message::SECTION_ANSWER,
cached_rrset);
answer_message->setRcode(Rcode::NOERROR());
crs->success(answer_message);
} else {
dlog("Message not found in cache, starting recursive query");
// It will delete itself when it is done
new RunningQuery(io, question, answer_message, upstream_, upstream_root_,
buffer, crs, query_timeout_, client_timeout_,
lookup_timeout_, retries_, nsas_, cache_);
}
}
}
......
......@@ -53,6 +53,7 @@ endif
run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
......
......@@ -21,7 +21,7 @@
/// convenience methods for accessing and updating the information.
#include <stdint.h>
#include "asiolink.h"
#include <asiolink/io_address.h>
namespace isc {
namespace nsas {
......@@ -39,7 +39,7 @@ public:
{}
/// \return Address object
asiolink::IOAddress getAddress() const {
const asiolink::IOAddress& getAddress() const {
return address_;
}
......
......@@ -18,41 +18,5 @@
#include <string>
#include <sys/socket.h>
namespace asiolink {
/// \brief IO Address Dummy Class
///
/// As part of ther resolver, Evan has written the asiolink.h file, which
/// encapsulates some of the boost::asio classes. Until these are checked
/// into trunk and merged with this branch, these dummy classes should fulfill
/// their function.
class IOAddress {
public:
/// \param address_str String representing the address
IOAddress(const std::string& address_str) : address_(address_str)
{}
/// \param Just a virtual destructor
virtual ~ IOAddress() { }
/// \return Textual representation of the address
std::string toText() const
{return address_;}
/// \return Address family of the address
virtual short getFamily() const {
return ((address_.find(".") != std::string::npos) ? AF_INET : AF_INET6);
}
/// \return true if two addresses are equal
bool equal(const IOAddress& address)
{return (toText() == address.toText());}
private:
std::string address_; ///< Address represented
};
} // namespace asiolink
#endif // __ASIOLINK_H
......@@ -35,6 +35,8 @@
#include <dns/question.h>
#include <resolve/resolver_interface.h>
#include <asiolink/io_address.h>
#include "address_entry.h"
#include "nameserver_address.h"
#include "nameserver_entry.h"
......@@ -140,7 +142,7 @@ NameserverEntry::setAddressRTT(const IOAddress& address, uint32_t rtt) {
AddressFamily family(V4_ONLY);
for (;;) {
BOOST_FOREACH(AddressEntry& entry, addresses_[family]) {
if (entry.getAddress().equal(address)) {
if (entry.getAddress().equals(address)) {
entry.setRTT(rtt);
return;
}
......@@ -181,7 +183,7 @@ NameserverEntry::updateAddressRTT(uint32_t rtt,
{
Lock lock(mutex_);
for (size_t i(0); i < addresses_[family].size(); ++ i) {
if (addresses_[family][i].getAddress().equal(address)) {
if (addresses_[family][i].getAddress().equals(address)) {
updateAddressRTTAtIndex(rtt, i, family);
return;
}
......@@ -226,8 +228,9 @@ class NameserverEntry::ResolverCallback :
response_message->getRcode() != isc::dns::Rcode::NOERROR() ||
response_message->getRRCount(isc::dns::Message::SECTION_ANSWER) == 0) {
failureInternal(lock);
return;
}
isc::dns::RRsetIterator rrsi =
response_message->beginSection(isc::dns::Message::SECTION_ANSWER);
const isc::dns::RRsetPtr response = *rrsi;
......
......@@ -54,6 +54,7 @@ endif
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
......
......@@ -24,12 +24,12 @@
#include <stdint.h>
#include "../asiolink.h"
#include <asiolink/io_address.h>
#include "../address_entry.h"
static std::string V4A_TEXT("1.2.3.4");
static std::string V4B_TEXT("5.6.7.8");
static std::string V6A_TEXT("2001:dead:beef::0");
static std::string V6A_TEXT("2001:dead:beef::");
static std::string V6B_TEXT("1984:1985::1986:1987");
using namespace asiolink;
......
......@@ -100,7 +100,7 @@ protected:
// Test that the address is equal to the address in NameserverEntry
TEST_F(NameserverAddressTest, Address) {
EXPECT_TRUE(ns_address_.getAddress().equal( ns_sample_.getAddressAtIndex(TEST_ADDRESS_INDEX)));
EXPECT_TRUE(ns_address_.getAddress().equals( ns_sample_.getAddressAtIndex(TEST_ADDRESS_INDEX)));
boost::shared_ptr<NameserverEntry> empty_ne((NameserverEntry*)NULL);
// It will throw an NullNameserverEntryPointer exception with the empty NameserverEntry shared pointer
......
......@@ -153,7 +153,7 @@ TEST_F(NameserverEntryTest, SetRTT) {
int matchcount = 0;
for (NameserverEntry::AddressVectorIterator i = newvec.begin();
i != newvec.end(); ++i) {
if (i->getAddress().equal(first_address)) {
if (i->getAddress().equals(first_address)) {
++matchcount;
EXPECT_EQ(i->getAddressEntry().getRTT(), new_rtt);
}
......@@ -188,7 +188,7 @@ TEST_F(NameserverEntryTest, Unreachable) {
int matchcount = 0;
for (NameserverEntry::AddressVectorIterator i = newvec.begin();
i != newvec.end(); ++i) {
if (i->getAddress().equal(first_address)) {
if (i->getAddress().equals(first_address)) {
++matchcount;
EXPECT_TRUE(i->getAddressEntry().isUnreachable());
}
......
......@@ -165,9 +165,9 @@ protected:
EXPECT_EQ(failure_count, callback_->unreachable_count_);
EXPECT_EQ(success_count, callback_->successes_.size());
for (size_t i = 0; i < callback_->successes_.size(); ++ i) {
EXPECT_TRUE(IOAddress("192.0.2.1").equal(
EXPECT_TRUE(IOAddress("192.0.2.1").equals(
callback_->successes_[i].getAddress()) ||
IOAddress("2001:db8::1").equal(
IOAddress("2001:db8::1").equals(
callback_->successes_[i].getAddress()));
}
}
......@@ -234,7 +234,7 @@ TEST_F(ZoneEntryTest, ChangedNS) {
EXPECT_NO_THROW(resolver_->answer(1, ns_name_, RRType::A(),
rdata::in::A("192.0.2.1")));
ASSERT_EQ(1, callback_->successes_.size());
EXPECT_TRUE(IOAddress("192.0.2.1").equal(
EXPECT_TRUE(IOAddress("192.0.2.1").equals(
callback_->successes_[0].getAddress()));
EXPECT_NO_THROW(resolver_->answer(2, ns_name_, RRType::AAAA(),
rdata::in::AAAA("2001:db8::1")));
......@@ -257,7 +257,7 @@ TEST_F(ZoneEntryTest, ChangedNS) {
EXPECT_NO_THROW(resolver_->answer(4, different_name, RRType::A(),
rdata::in::A("192.0.2.2")));
ASSERT_EQ(2, callback_->successes_.size());
EXPECT_TRUE(IOAddress("192.0.2.2").equal(
EXPECT_TRUE(IOAddress("192.0.2.2").equals(
callback_->successes_[1].getAddress()));
// And now, switch back, as it timed out again
......@@ -270,7 +270,7 @@ TEST_F(ZoneEntryTest, ChangedNS) {
EXPECT_EQ(7, resolver_->requests.size());
EXPECT_EQ(Fetchable::READY, zone->getState());
ASSERT_EQ(3, callback_->successes_.size());
EXPECT_TRUE(IOAddress("192.0.2.1").equal(
EXPECT_TRUE(IOAddress("192.0.2.1").equals(
callback_->successes_[0].getAddress()));
}
......@@ -306,9 +306,9 @@ TEST_F(ZoneEntryTest, CallbacksAnswered) {
rdata::in::A("192.0.2.1")));
// Two are answered (ANY and V4)
ASSERT_EQ(2, callback_->successes_.size());
EXPECT_TRUE(IOAddress("192.0.2.1").equal(
EXPECT_TRUE(IOAddress("192.0.2.1").equals(
callback_->successes_[0].getAddress()));
EXPECT_TRUE(IOAddress("192.0.2.1").equal(
EXPECT_TRUE(IOAddress("192.0.2.1").equals(
callback_->successes_[1].getAddress()));
// None are rejected
EXPECT_EQ(0, callback_->unreachable_count_);
......@@ -318,7 +318,7 @@ TEST_F(ZoneEntryTest, CallbacksAnswered) {
// This should answer the third callback
EXPECT_EQ(0, callback_->unreachable_count_);
ASSERT_EQ(3, callback_->successes_.size());
EXPECT_TRUE(IOAddress("2001:db8::1").equal(
EXPECT_TRUE(IOAddress("2001:db8::1").equals(
callback_->successes_[2].getAddress()));
// It should think it is ready
EXPECT_EQ(Fetchable::READY, zone->getState());
......@@ -326,7 +326,7 @@ TEST_F(ZoneEntryTest, CallbacksAnswered) {
zone->addCallback(callback_, V4_ONLY);
EXPECT_EQ(3, resolver_->requests.size());
ASSERT_EQ(4, callback_->successes_.size());
EXPECT_TRUE(IOAddress("192.0.2.1").equal(
EXPECT_TRUE(IOAddress("192.0.2.1").equals(
callback_->successes_[3].getAddress()));
EXPECT_EQ(0, callback_->unreachable_count_);
}
......@@ -366,9 +366,9 @@ TEST_F(ZoneEntryTest, CallbacksAOnly) {
EXPECT_NO_THROW(resolver_->answer(1, ns_name_, RRType::A(),
rdata::in::A("192.0.2.1")));
ASSERT_EQ(2, callback_->successes_.size());
EXPECT_TRUE(IOAddress("192.0.2.1").equal(
EXPECT_TRUE(IOAddress("192.0.2.1").equals(
callback_->successes_[0].getAddress()));
EXPECT_TRUE(IOAddress("192.0.2.1").equal(
EXPECT_TRUE(IOAddress("192.0.2.1").equals(
callback_->successes_[1].getAddress()));
EXPECT_EQ(1, callback_->unreachable_count_);
// Everything arriwed, so we are ready
......@@ -377,7 +377,7 @@ TEST_F(ZoneEntryTest, CallbacksAOnly) {
zone->addCallback(callback_, V4_ONLY);
EXPECT_EQ(3, resolver_->requests.size());
ASSERT_EQ(3, callback_->successes_.size());
EXPECT_TRUE(IOAddress("192.0.2.1").equal(
EXPECT_TRUE(IOAddress("192.0.2.1").equals(
callback_->successes_[2].getAddress()));
EXPECT_EQ(1, callback_->unreachable_count_);
......@@ -439,9 +439,9 @@ TEST_F(ZoneEntryTest, CallbackTwoNS) {
// The other callbacks should be answered now
EXPECT_EQ(2, callback_->unreachable_count_);
ASSERT_EQ(2, callback_->successes_.size());
EXPECT_TRUE(IOAddress("2001:db8::1").equal(
EXPECT_TRUE(IOAddress("2001:db8::1").equals(
callback_->successes_[0].getAddress()));
EXPECT_TRUE(IOAddress("2001:db8::1").equal(
EXPECT_TRUE(IOAddress("2001:db8::1").equals(
callback_->successes_[1].getAddress()));
}
......@@ -534,7 +534,7 @@ TEST_F(ZoneEntryTest, AddressTimeout) {
rdata::in::A("192.0.2.1"), 0));
// It answers, not rejects
ASSERT_EQ(1, callback_->successes_.size());
EXPECT_TRUE(IOAddress("192.0.2.1").equal(
EXPECT_TRUE(IOAddress("192.0.2.1").equals(
callback_->successes_[0].getAddress()));
EXPECT_EQ(0, callback_->unreachable_count_);