Browse Source

Merge branch 'trac501'

Jelte Jansen 14 years ago
parent
commit
82f0bc8ecd

+ 1 - 0
src/lib/nsas/Makefile.am

@@ -37,5 +37,6 @@ libnsas_la_SOURCES += zone_entry.cc zone_entry.h
 libnsas_la_SOURCES += fetchable.h
 libnsas_la_SOURCES += address_request_callback.h
 libnsas_la_SOURCES += random_number_generator.h
+libnsas_la_SOURCES += glue_hints.h glue_hints.cc
 
 CLEANFILES = *.gcno *.gcda

+ 164 - 0
src/lib/nsas/glue_hints.cc

@@ -0,0 +1,164 @@
+// 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 "glue_hints.h"
+
+#include <dns/rrset.h>
+#include <dns/rdata.h>
+#include <dns/rrtype.h>
+#include <dns/rdataclass.h>
+
+#include <asiolink/io_address.h>
+#include <nsas/nameserver_entry.h>
+
+using namespace isc::dns;
+using namespace isc::nsas;
+
+// This is a simple implementation for finding glue
+//
+// It iterates over the AUTHORITY section of the given Message,
+// and for each NS RR it iterates over the ADDITIONAL section to
+// see if there are A or AAAA records.
+//
+// Of course, this could be done more efficiently. One option is to
+// reverse this; check for A and AAAA records (since those will only
+// be there if there actually is glue, while NS records will be present
+// in any delegation). However, it may be even better to let the
+// Response Classifier decide on glue, while it is validating the packet
+//
+// (er, TODO, so to speak. discuss.)
+
+// Helper functions
+namespace {
+    // Add the contents of the given A or AAAA rrset to the given
+    // addressvector
+    //
+    // This creates an 'dummy' NameserverEntry value, because that
+    // is enforced by NameserverAddress. We may want to reconsider
+    // the need for that (perhaps we can change it so that if it is
+    // NULL, all NSAS-related calls to the NameserverAddress object
+    // become nops)
+    void
+    addRRset(std::vector<NameserverAddress>& addresses,
+             const RRsetPtr rrset)
+    {
+        const std::string ns_name = rrset->getName().toText();
+        RdataIteratorPtr rdi = rrset->getRdataIterator();
+        while (!rdi->isLast()) {
+            AddressEntry entry(asiolink::IOAddress(rdi->getCurrent().toText()));
+            boost::shared_ptr<NameserverEntry> ns_entry(new NameserverEntry(ns_name, rrset->getClass()));
+            NameserverAddress ns_address(ns_entry, entry, V4_ONLY);
+            addresses.push_back(ns_address);
+            rdi->next();
+        }
+    }
+}
+
+namespace isc {
+namespace nsas {
+
+GlueHints::GlueHints(const std::string& zone_name,
+                     const isc::dns::Message& delegation_message)
+{
+    for (RRsetIterator rssi = delegation_message.beginSection(Message::SECTION_AUTHORITY);
+         rssi != delegation_message.endSection(Message::SECTION_AUTHORITY);
+         ++rssi) {
+        if ((*rssi)->getType() == RRType::NS() &&
+            (*rssi)->getName().toText() == zone_name) {
+            addGlueForRRset(*rssi, delegation_message);
+        }
+    }
+}
+
+
+bool
+GlueHints::hasGlue(AddressFamily family) const {
+    return ((addresses_v4.size() > 0 && (family == ANY_OK || family == V4_ONLY)) ||
+            (addresses_v6.size() > 0 && (family == ANY_OK || family == V6_ONLY)));
+}
+
+NameserverAddress
+GlueHints::getGlue(AddressFamily family) const {
+    // TODO: once we have a more general random lib, use that. Since
+    // this is simply glue, and we don't need a weighted selection,
+    // for now srandom should be good enough. Once #583 has been merged,
+    // (or better yet, once that one and the weighted random have gone
+    // together in a util lib), we can use that.
+    int max = 0;
+    size_t v4s = addresses_v4.size();
+    size_t v6s = addresses_v6.size();
+
+    if (family == ANY_OK || family == V4_ONLY) {
+        max += v4s;
+    }
+    if (family == ANY_OK || family == V6_ONLY) {
+        max += v6s;
+    }
+
+    assert(max > 0);
+    long int selection = random() % max;
+
+    if (family == ANY_OK) {
+        if (selection <= v4s) {
+            return addresses_v4[selection];
+        } else {
+            return addresses_v6[selection-v4s];
+        }
+    } else if (family == V4_ONLY) {
+        return addresses_v4[selection];
+    } else if (family == V6_ONLY) {
+        return addresses_v6[selection];
+    } else {
+        // Unknown family
+        assert(false);
+    }
+}
+
+// Add the A and AAAA records from the given message for the given
+// NS name to the relevant address vector
+// (A rrsets are added to addresses_v4, AAAA rrsets are added to
+// addresses_v6).
+void
+GlueHints::addGlueForName(const Name& name, const Message& message)
+{
+    for (RRsetIterator rssi = message.beginSection(Message::SECTION_ADDITIONAL);
+         rssi != message.endSection(Message::SECTION_ADDITIONAL);
+         ++rssi) {
+        if ((*rssi)->getName() == name) {
+            if ((*rssi)->getType() == RRType::A()) {
+                addRRset(addresses_v4, *rssi);
+            } else if ((*rssi)->getType() == RRType::AAAA()) {
+                addRRset(addresses_v6, *rssi);
+            }
+        }
+    }
+}
+
+// Add the glue for the given NS RRset in the message to the
+// relevant vectors.
+void
+GlueHints::addGlueForRRset(const RRsetPtr rrset, const Message& message)
+{
+    RdataIteratorPtr rdi = rrset->getRdataIterator();
+    while (!rdi->isLast()) {
+        isc::dns::Name name(dynamic_cast<const rdata::generic::NS&>(
+                        rdi->getCurrent()).getNSName());
+        addGlueForName(name, message);
+        rdi->next();
+    }
+}
+
+
+} // namespace nsas
+} // namespace isc

+ 71 - 0
src/lib/nsas/glue_hints.h

@@ -0,0 +1,71 @@
+// 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 __GLUE_HINTS_H
+#define __GLUE_HINTS_H
+
+#include <vector>
+
+#include <dns/message.h>
+
+#include "nsas_types.h"
+#include "nameserver_address.h"
+
+namespace isc {
+namespace nsas {
+
+class GlueHints {
+public:
+    /// \brief Empty constructor
+    GlueHints() {};
+
+    /// \brief Constructor
+    ///
+    /// Creates a glue hint object, with the glue data found in the
+    /// given packet.
+    ///
+    /// \param zone_name The name of the zone to find glue for
+    /// \param delegation_message The Message that may contain glue
+    GlueHints(const std::string& zone_name,
+              const isc::dns::Message& delegation_message);
+
+    /// \brief Check if there is glue for the given AddressFamily
+    ///
+    /// \param family the AddressFamily to check for glue for
+    /// \return true if there is glue for that family. false if not
+    bool hasGlue(AddressFamily family) const;
+
+    /// \brief Get a random glue address for the given family
+    ///
+    /// ONLY call this if hasGlue() returned true.
+    ///
+    /// \param family the AddressFamily to get glue for
+    /// \return a NameserverAddress specified by the glue
+    NameserverAddress getGlue(AddressFamily family) const;
+
+private:
+    void addGlueForName(const isc::dns::Name& name,
+                        const isc::dns::Message& message);
+    void addGlueForRRset(const isc::dns::RRsetPtr rrset,
+                         const isc::dns::Message& message);
+
+    std::vector<NameserverAddress> addresses_v4;
+    std::vector<NameserverAddress> addresses_v6;
+};
+
+}
+}
+
+
+#endif // __GLUE_HINTS_H

+ 5 - 2
src/lib/nsas/nameserver_address_store.cc

@@ -29,6 +29,7 @@
 #include "nameserver_entry.h"
 #include "nameserver_address_store.h"
 #include "zone_entry.h"
+#include "glue_hints.h"
 #include "address_request_callback.h"
 
 using namespace isc::dns;
@@ -80,7 +81,8 @@ newZone(
 
 void
 NameserverAddressStore::lookup(const string& zone, const RRClass& class_code,
-    boost::shared_ptr<AddressRequestCallback> callback, AddressFamily family)
+    boost::shared_ptr<AddressRequestCallback> callback, AddressFamily family,
+    const GlueHints glue_hints)
 {
     pair<bool, boost::shared_ptr<ZoneEntry> > zone_obj(zone_hash_->getOrAdd(HashKey(
         zone, class_code), boost::bind(newZone, &resolver_, &zone, &class_code,
@@ -90,7 +92,8 @@ NameserverAddressStore::lookup(const string& zone, const RRClass& class_code,
     } else {
         zone_lru_->touch(zone_obj.second);
     }
-    zone_obj.second->addCallback(callback, family);
+    
+    zone_obj.second->addCallback(callback, family, glue_hints);
 }
 
 void

+ 2 - 1
src/lib/nsas/nameserver_address_store.h

@@ -23,6 +23,7 @@
 #include <resolve/resolver_interface.h>
 
 #include "nsas_types.h"
+#include "glue_hints.h"
 
 namespace isc {
 // Some forward declarations, so we do not need to include so many headers
@@ -85,7 +86,7 @@ public:
     /// \param family Which address is requested.
     void lookup(const std::string& zone, const dns::RRClass& class_code,
         boost::shared_ptr<AddressRequestCallback> callback, AddressFamily
-        family = ANY_OK);
+        family = ANY_OK, const GlueHints = GlueHints());
 
     /// \brief cancel the given lookup action
     ///

+ 11 - 3
src/lib/nsas/zone_entry.cc

@@ -224,7 +224,8 @@ class ZoneEntry::ResolverCallback :
 };
 
 void
-ZoneEntry::addCallback(CallbackPtr callback, AddressFamily family) {
+ZoneEntry::addCallback(CallbackPtr callback, AddressFamily family,
+                       const GlueHints glue_hints) {
     Lock lock(mutex_);
 
     bool ask(false);
@@ -238,11 +239,18 @@ ZoneEntry::addCallback(CallbackPtr callback, AddressFamily family) {
     if (getState() == EXPIRED || getState() == NOT_ASKED) {
         ask = true;
     }
-
+    
     // We do not have the answer right away, just queue the callback
     bool execute(!ask && getState() != IN_PROGRESS &&
         callbacks_[family].empty());
-    callbacks_[family].push_back(callback);
+
+    // Unless there was glue
+    if (ask && glue_hints.hasGlue(family)) {
+        callback->success(glue_hints.getGlue(family));
+    } else {
+        callbacks_[family].push_back(callback);
+    }
+
     if (execute) {
         // Try to process it right away, store if not possible to handle
         process(family, NameserverPtr());

+ 6 - 1
src/lib/nsas/zone_entry.h

@@ -32,6 +32,7 @@
 #include "fetchable.h"
 #include "nsas_types.h"
 #include "random_number_generator.h"
+#include "glue_hints.h"
 
 namespace isc {
 namespace nsas {
@@ -97,9 +98,13 @@ public:
      *
      * \param callback The callback itself.
      * \param family Which address family is acceptable as an answer?
+     * \param glue_hints If a non-empty glue-hints object is passed,
+     *        and the NSAS does not have an immediate answer, it will
+     *        call back immediately with one of the glue hints.
      */
     void addCallback(boost::shared_ptr<AddressRequestCallback>
-        callback, AddressFamily family);
+        callback, AddressFamily family,
+        const GlueHints glue_hints = GlueHints());
 
     /**
      * \short Remove a callback from the list

+ 13 - 5
src/lib/resolve/recursive_query.cc

@@ -330,7 +330,7 @@ private:
             isc::resolve::ResponseClassifier::classify(
                 question_, incoming, cname_target, cname_count_);
 
-        bool found_ns_address = false;
+        bool found_ns = false;
             
         switch (category) {
         case isc::resolve::ResponseClassifier::ANSWER:
@@ -384,7 +384,7 @@ private:
             // 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 != incoming.endSection(Message::SECTION_AUTHORITY) && !found_ns;
                  ++rrsi) {
                 ConstRRsetPtr rrs = *rrsi;
                 if (rrs->getType() == RRType::NS()) {
@@ -393,18 +393,26 @@ private:
                     // libraries, so as not to need many conversions)
                     cur_zone_ = rrs->getName().toText();
                     dlog("Referred to zone " + cur_zone_);
-                    found_ns_address = true;
+                    found_ns = true;
                     break;
                 }
             }
 
-            if (found_ns_address) {
+            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.
-                send();
+                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 {
                 dlog("No NS RRset in referral?");