merged PingRotate into PingScheduler; fixed save/load of cache to/from file; started...
[pingcheck] / src / dns / dnscache.cpp
index d516ae2..d306fd1 100644 (file)
@@ -58,6 +58,11 @@ Cname::Cname(const std::string &host, const uint32_t ttl)
     , Ttl( ttl )
 {}
 
+Cname::Cname(const std::string &host, const TimeToLive &ttl)
+    : Host( host )
+    , Ttl( ttl )
+{}
+
 DnsCache::DnsCache(const IoServiceItem &io_serv,
                    const std::string &cache_file)
     : IpCache()
@@ -78,7 +83,7 @@ DnsCache::DnsCache(const IoServiceItem &io_serv,
 
 DnsCache::~DnsCache()
 {
-    GlobalLogger.info() << "Dns Cache is being destructed";
+    GlobalLogger.info() << "DnsCache: being destructed";
 
     // save one last time without re-scheduling the next save
     save_to_cachefile();
@@ -117,109 +122,121 @@ void DnsCache::schedule_save(const boost::system::error_code &error)
 void DnsCache::save_to_cachefile()
 {
     if (!HasChanged)
-    {
         GlobalLogger.info() << "DnsCache: skip saving because has not changed";
-        return;
-    }
     else if (CacheFile.empty())
-    {
         GlobalLogger.warning()
                            << "DnsCache: skip saving because file name empty!";
-        return;
-    }
-
-    try
-    {
-        std::ofstream ofs( CacheFile.c_str() );
-        boost::archive::xml_oarchive oa(ofs);
-        oa << boost::serialization::make_nvp("IpCache", IpCache);
-        oa << boost::serialization::make_nvp("CnameCache", CnameCache);
-        GlobalLogger.info() << "DnsCache: saved to cache file " << CacheFile;
-
-        HasChanged = false;
-    }
-    catch (std::exception &exc)
+    else if (CacheFile == DO_NOT_USE_CACHE_FILE)
+        GlobalLogger.info() << "DnsCache: configured not to use cache file";
+    else
     {
-        GlobalLogger.warning() << "Saving failed: " << exc.what();
+        try
+        {
+            std::ofstream ofs( CacheFile.c_str() );
+            boost::archive::xml_oarchive oa(ofs);
+            //oa << boost::serialization::make_nvp("IpCache", IpCache);
+            //oa << boost::serialization::make_nvp("CnameCache", CnameCache);
+            oa & BOOST_SERIALIZATION_NVP(IpCache);
+            oa & BOOST_SERIALIZATION_NVP(CnameCache);
+            GlobalLogger.info() << "DnsCache: saved to cache file " << CacheFile;
+
+            HasChanged = false;
+        }
+        catch (std::exception &exc)
+        {
+            GlobalLogger.warning() << "Saving failed: " << exc.what();
+        }
     }
 }
 
-
 void DnsCache::load_from_cachefile()
 {
     if (CacheFile.empty())
-    {
         GlobalLogger.warning()
                   << "DnsCache: cannot load because cache file name is empty!";
-        return;
-    }
+    else if (CacheFile == DO_NOT_USE_CACHE_FILE)
+        GlobalLogger.info() << "DnsCache: configured not to use cache file";
     else if ( !I2n::file_exists(CacheFile) )
-    {
         GlobalLogger.warning() << "DnsCache: cannot load because cache file "
                                << CacheFile << " does not exist!";
-        return;
-    }
-    try
+    else
     {
-        HostAddressVec cache;
-
-        std::ifstream ifs( CacheFile.c_str() );
-        boost::archive::xml_iarchive ia(ifs);
+        try
+        {
+            std::ifstream ifs( CacheFile.c_str() );
+            boost::archive::xml_iarchive ia(ifs);
 
-        ia >> boost::serialization::make_nvp("IpCache", cache);
-        ia >> boost::serialization::make_nvp("CnameCache", cache);
-        GlobalLogger.info() << "DnsCache: loaded from file " << CacheFile;
-    }
-    catch (boost::archive::archive_exception &exc)
-    {
-        GlobalLogger.warning() << "DnsCache: archive exception loading from "
-                               << CacheFile << ": " << exc.what();
-    }
-    catch (std::exception &exc)
-    {
-        GlobalLogger.warning() << "DnsCache: exception while loading from "
-                               << CacheFile << ": " << exc.what();
+            ia & BOOST_SERIALIZATION_NVP(IpCache);
+            ia & BOOST_SERIALIZATION_NVP(CnameCache);
+            GlobalLogger.info() << "DnsCache: loaded from file " << CacheFile;
+        }
+        catch (boost::archive::archive_exception &exc)
+        {
+            GlobalLogger.warning() << "DnsCache: archive exception loading from "
+                                   << CacheFile << ": " << exc.what();
+        }
+        catch (std::exception &exc)
+        {
+            GlobalLogger.warning() << "DnsCache: exception while loading from "
+                                   << CacheFile << ": " << exc.what();
+        }
     }
 }
 
-void DnsCache::update(const std::string &hostname,
-                      const HostAddressVec &new_data)
+
+// warn if hostname is empty and remove trailing dot
+std::string DnsCache::key_for_hostname(const std::string &hostname) const
 {
-    GlobalLogger.info() << "DnsCache: update IPs for " << hostname
-                        << " to " << new_data.size() << "-list";
-    IpCache[hostname] = new_data;
-    HasChanged = true;
+    if (hostname.empty())
+    {
+        GlobalLogger.warning() << "DnsCache: empty host!";
+        return "";
+    }
+
+    // check whether last character is a dot
+    if (hostname.rfind('.') == hostname.length()-1)
+        return hostname.substr(0, hostname.length()-1);
+    else
+        return hostname;
 }
 
 
 void DnsCache::update(const std::string &hostname,
-                      const Cname &cname)
+                      const HostAddressVec &new_ips)
 {
-    GlobalLogger.info() << "DnsCache: update CNAME for " << hostname
-                        << " to " << cname.Host;
-    CnameCache[hostname] = cname;
+    std::string key = key_for_hostname(hostname);
+    if ( !get_cname(hostname).Host.empty() )
+    {   // ensure that there is never IP and CNAME for the same host
+        GlobalLogger.warning() << "DnsCache: Saving IPs for " << key
+            << " removes CNAME to " << get_cname(hostname).Host << "!";
+        update(hostname, Cname());   // overwrite with "empty" cname
+    }
+    GlobalLogger.debug() << "DnsCache: update IPs for " << key
+                        << " to " << new_ips.size() << "-list";
+    IpCache[key] = new_ips;
     HasChanged = true;
 }
 
 
 void DnsCache::update(const std::string &hostname,
-                          const uint32_t new_ttl)
+                      const Cname &cname)
 {
-    GlobalLogger.info() << "DnsCache: ensure TTL for IPs for " << hostname
-                        << " is below " << new_ttl << "s";
-    HostAddressVec ips = IpCache[hostname];
-    TimeToLive current_ttl;
-    BOOST_FOREACH( HostAddress &addr, ips )
-    {
-        current_ttl = addr.get_ttl();
-        if (current_ttl.get_value() > new_ttl)
-        {
-            current_ttl.set_value(new_ttl);
-            addr.set_ttl(current_ttl);
-            HasChanged = true;
-        }
+    std::string key = key_for_hostname(hostname);
+    if ( !get_ips(hostname).empty() )
+    {   // ensure that there is never IP and CNAME for the same host
+        GlobalLogger.warning() << "DnsCache: Saving CNAME for " << key
+            << " removes " << get_ips(hostname).size() << " IPs for same host!";
+        update(hostname, HostAddressVec());   // overwrite with empty IP list
     }
-    IpCache[hostname] = ips;
+
+    // remove possible trailing dot from cname
+    Cname to_save = Cname(key_for_hostname(cname.Host),
+                          cname.Ttl);
+
+    GlobalLogger.info() << "DnsCache: update CNAME for " << key
+                        << " to " << to_save.Host;
+    CnameCache[key] = to_save;
+    HasChanged = true;
 }
 
 
@@ -229,24 +246,32 @@ void DnsCache::update(const std::string &hostname,
 HostAddressVec DnsCache::get_ips(const std::string &hostname,
                                  const bool check_up_to_date)
 {
-    HostAddressVec result = IpCache[hostname];
+    std::string key = key_for_hostname(hostname);
+    HostAddressVec result = IpCache[key];
     if (check_up_to_date)
     {
         HostAddressVec result_up_to_date;
         uint32_t threshold = static_cast<uint32_t>(
                 DnsMaster::get_instance()->get_resolved_ip_ttl_threshold() );
+        uint32_t updated_ttl;
         BOOST_FOREACH( const HostAddress &addr, result )
         {
-            if (addr.get_ttl().get_updated_value() > threshold)
+            updated_ttl = addr.get_ttl().get_updated_value();
+            if (updated_ttl > threshold)
                 result_up_to_date.push_back(addr);
+            else
+                GlobalLogger.debug() << "DnsCache: do not return "
+                    << addr.get_ip().to_string() << " since TTL "
+                    << updated_ttl << "s is out of date (thresh="
+                    << threshold << "s)";
         }
-        GlobalLogger.debug() << "DnsCache: From cached list of size "
-             << result.size() << " return " << result_up_to_date.size()
-             << " since rest out of date";
         result = result_up_to_date;
     }
-    GlobalLogger.info() << "DnsCache: request IPs for " << hostname
-                        << " --> " << result.size() << "-list";
+    GlobalLogger.debug() << "DnsCache: request IPs for " << key
+                         << " --> " << result.size() << "-list";
+    BOOST_FOREACH( const HostAddress &addr, result )
+        GlobalLogger.debug() << "DnsCache:    " << addr.get_ip().to_string()
+                             << " (TTL " << addr.get_ttl().get_value() << "s)";
     return result;
 }
 
@@ -256,16 +281,17 @@ HostAddressVec DnsCache::get_ips(const std::string &hostname,
 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.Host << "\"";
+    std::string key = key_for_hostname(hostname);
+    Cname result_obj = CnameCache[key];
+    GlobalLogger.debug() << "DnsCache: request CNAME for " << key
+                         << " --> \"" << result_obj.Host << "\"";
     if (result_obj.Host.empty())
         return result_obj;
 
     else if (check_up_to_date)
     {
-        if (result_obj.Ttl.get_updated_value() > DnsMaster::get_instance()
-                                              ->get_resolved_ip_ttl_threshold())
+        if ( result_obj.Ttl.get_updated_value() > static_cast<uint32_t>(
+                   DnsMaster::get_instance()->get_resolved_ip_ttl_threshold()) )
             return result_obj;
         else
         {
@@ -290,7 +316,7 @@ HostAddressVec DnsCache::get_ips_recursive(const std::string &hostname,
     while ( result.empty() )
     {
         current_cname = get_cname(current_host, check_up_to_date);
-        current_host = current_cname.Host;
+        current_host = key_for_hostname(current_cname.Host);
         if (current_host.empty())
             break;
         else if (++n_recursions >= Config::MaxRetrievalRecursions)
@@ -353,7 +379,7 @@ std::string DnsCache::get_first_outdated_cname(const std::string &hostname,
         }
 
         cname = get_cname(up_to_date_host);
-        if (cname.Host.empty())
+        if (key_for_hostname(cname.Host).empty())
             // reached end of cname list
             break;
         else if (cname.Ttl.get_updated_value() > ttl_thresh)