Browse Source

[trac501] Pass glue hints to the NSAS

It comes in the form of a GlueHints object (see glue_hints.[h|cc]), which is created with a source delegation packet. This gets passed on to addCallback, and if the NSAS notices it has to do work (i.e. it does not have data available), it'll use the glue (if that is available).

Changed on argument shared_ptr ref to a normal shared_ptr in NamserverAddress constructor; need the refcount to be incremented.
Jelte Jansen 14 years ago
parent
commit
610400fa75

+ 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

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

@@ -0,0 +1,169 @@
+// 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();
+        }
+    }
+
+    // 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
+    addGlueForName(std::vector<NameserverAddress>& addresses_v4,
+                   std::vector<NameserverAddress>& addresses_v6,
+                   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
+    addGlueForRRset(std::vector<NameserverAddress>& addresses_v4,
+                    std::vector<NameserverAddress>& addresses_v6,
+                    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(addresses_v4, addresses_v6, name, message);
+            rdi->next();
+        }
+    }
+}
+
+namespace isc {
+namespace nsas {
+
+GlueHints::GlueHints(const std::string& zone_name,
+                     const isc::dns::Message& delegation_message)
+{
+    std::cout << delegation_message.toText();
+    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(addresses_v4, addresses_v6,
+                            *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);
+    }
+}
+
+} // namespace nsas
+} // namespace isc

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

@@ -0,0 +1,67 @@
+// 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:
+
+    std::vector<NameserverAddress> addresses_v4;
+    std::vector<NameserverAddress> addresses_v6;
+};
+
+}
+}
+
+
+#endif // __GLUE_HINTS_H

+ 1 - 1
src/lib/nsas/nameserver_address.h

@@ -65,7 +65,7 @@ public:
     /// request is processing.
     /// \param index The address's index in NameserverEntry's addresses vector
     /// \param family Address family, V4_ONLY or V6_ONLY
-    NameserverAddress(const boost::shared_ptr<NameserverEntry>& nameserver,
+    NameserverAddress(const boost::shared_ptr<NameserverEntry> nameserver,
         const AddressEntry& address, AddressFamily family):
         ns_(nameserver), address_(address), family_(family)
     {

+ 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

+ 9 - 1
src/lib/resolve/recursive_query.cc

@@ -365,7 +365,15 @@ private:
                 // 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?");