simplified dns (no self-made recursion); merge PingScheduler and PingRotate; make...
[pingcheck] / src / dns / dnscache.cpp
index e87c039..d516ae2 100644 (file)
@@ -48,6 +48,16 @@ namespace Config
     int MaxRetrievalRecursions = 10;
 }
 
+Cname::Cname()
+    : Host()
+    , Ttl()
+{}
+
+Cname::Cname(const std::string &host, const uint32_t ttl)
+    : Host( host )
+    , Ttl( ttl )
+{}
+
 DnsCache::DnsCache(const IoServiceItem &io_serv,
                    const std::string &cache_file)
     : IpCache()
@@ -186,7 +196,7 @@ void DnsCache::update(const std::string &hostname,
                       const Cname &cname)
 {
     GlobalLogger.info() << "DnsCache: update CNAME for " << hostname
-                        << " to " << cname.first;
+                        << " to " << cname.Host;
     CnameCache[hostname] = cname;
     HasChanged = true;
 }
@@ -213,6 +223,9 @@ void DnsCache::update(const std::string &hostname,
 }
 
 
+/**
+ * @returns empty list if no (up to date) ips for hostname in cache
+ */
 HostAddressVec DnsCache::get_ips(const std::string &hostname,
                                  const bool check_up_to_date)
 {
@@ -220,8 +233,8 @@ HostAddressVec DnsCache::get_ips(const std::string &hostname,
     if (check_up_to_date)
     {
         HostAddressVec result_up_to_date;
-        int threshold = DnsMaster::get_instance()
-                        ->get_resolved_ip_ttl_threshold();
+        uint32_t threshold = static_cast<uint32_t>(
+                DnsMaster::get_instance()->get_resolved_ip_ttl_threshold() );
         BOOST_FOREACH( const HostAddress &addr, result )
         {
             if (addr.get_ttl().get_updated_value() > threshold)
@@ -237,25 +250,31 @@ HostAddressVec DnsCache::get_ips(const std::string &hostname,
     return result;
 }
 
-std::string DnsCache::get_cname(const std::string &hostname,
-                                 const bool check_up_to_date)
+/**
+ * @returns empty cname if no (up to date cname) for hostname in cache
+ */
+Cname DnsCache::get_cname(const std::string &hostname,
+                          const bool check_up_to_date)
 {
     Cname result_obj = CnameCache[hostname];
     GlobalLogger.info() << "DnsCache: request CNAME for " << hostname
-                        << " --> \"" << result_obj.first << "\"";
-    if (check_up_to_date)
+                        << " --> \"" << result_obj.Host << "\"";
+    if (result_obj.Host.empty())
+        return result_obj;
+
+    else if (check_up_to_date)
     {
-        if (result_obj.second.get_updated_value() > DnsMaster::get_instance()
+        if (result_obj.Ttl.get_updated_value() > DnsMaster::get_instance()
                                               ->get_resolved_ip_ttl_threshold())
-            return result_obj.first;
+            return result_obj;
         else
         {
-            GlobalLogger.debug() << "DnsCache: Cname is out of date";
-            return "";
+            GlobalLogger.debug() << "DnsCache: CNAME is out of date";
+            return Cname();    // same as if there was no cname for hostname
         }
     }
     else
-        return result_obj.first;
+        return result_obj;
 }
 
 // underlying assumption in this function: for a hostname, the cache has either
@@ -264,11 +283,14 @@ HostAddressVec DnsCache::get_ips_recursive(const std::string &hostname,
                                            const bool check_up_to_date)
 {
     std::string current_host = hostname;
+    Cname current_cname;
     HostAddressVec result = get_ips(current_host);
     int n_recursions = 0;
+    uint32_t min_cname_ttl = 0xffff;   // largest possible unsigned 4-byte value
     while ( result.empty() )
     {
-        current_host = get_cname(current_host, check_up_to_date);
+        current_cname = get_cname(current_host, check_up_to_date);
+        current_host = current_cname.Host;
         if (current_host.empty())
             break;
         else if (++n_recursions >= Config::MaxRetrievalRecursions)
@@ -278,10 +300,71 @@ HostAddressVec DnsCache::get_ips_recursive(const std::string &hostname,
             break;
         }
         else
+        {
+            min_cname_ttl = min(min_cname_ttl, current_cname.Ttl.get_value());
             result = get_ips(current_host, check_up_to_date);
+        }
+    }
+
+    GlobalLogger.debug() << "DnsCache: recursive IP retrieval resulted in "
+                         << result.size() << "-list after " << n_recursions
+                         << " recursions";
+
+    // adjust ttl to min of ttl and min_cname_ttl
+    if (n_recursions > 0)
+    {
+        TimeToLive cname_ttl(min_cname_ttl);
+
+        BOOST_FOREACH( HostAddress &addr, result )
+        {
+            if (addr.get_ttl().get_value() > min_cname_ttl)
+                addr.set_ttl(cname_ttl);
+        }
     }
+
     return result;
 }
 
+/**
+ * from a list of CNAMEs find the first one that is out of date
+ *
+ * required in ResolverBase::get_skipper
+ * 
+ * Assume you have the following situation in cache with TTLs below:
+ * hostname --> cname1 --> cname2 --> ... --> cnameN [--> IP]
+ *               100         0                                --> return cname2
+ *               100        100        100     100            --> return cnameN
+ *    ( with N < Config::MaxRetrievalRecursions )
+ * hostname --> IP (no cnames involved) --> return hostname
+ */
+std::string DnsCache::get_first_outdated_cname(const std::string &hostname,
+                                               const uint32_t ttl_thresh)
+{
+    std::string up_to_date_host = hostname;
+    Cname cname;
+    int n_recursions = 0;
+    while (true)
+    {
+        if (++n_recursions >= Config::MaxRetrievalRecursions)
+        {
+            GlobalLogger.warning() << "DnsCache: reached recursion limit of "
+                << n_recursions << " in search of outdated CNAMEs!";
+            break;
+        }
+
+        cname = get_cname(up_to_date_host);
+        if (cname.Host.empty())
+            // reached end of cname list
+            break;
+        else if (cname.Ttl.get_updated_value() > ttl_thresh)
+            // cname is up to date --> continue looking
+            up_to_date_host = cname.Host;
+        else
+            // cname is out of date --> return that
+            break;
+    }
+    return up_to_date_host;
+}
+
 // (created using vim -- the world's best text editor)