2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
20 Christian Herdtweck, Intra2net AG 2015
23 #include "dns/dnscache.h"
26 #include <logfunc.hpp>
27 #include <filefunc.hxx> // I2n::file_exists
28 #include <boost/foreach.hpp>
29 #include <boost/bind.hpp>
30 #include <boost/date_time/posix_time/posix_time.hpp>
31 #include <boost/asio/placeholders.hpp>
32 #include <boost/serialization/serialization.hpp>
33 #include <boost/serialization/map.hpp>
34 #include <boost/serialization/string.hpp>
35 #include <boost/serialization/vector.hpp>
36 #include <boost/archive/xml_oarchive.hpp>
37 #include <boost/archive/xml_iarchive.hpp>
39 #include "dns/dnsmaster.h"
42 using boost::posix_time::seconds;
43 using I2n::Logger::GlobalLogger;
47 int SaveTimerSeconds = 60;
48 int MaxRetrievalRecursions = 10;
51 DnsCache::DnsCache(const IoServiceItem &io_serv,
52 const std::string &cache_file)
55 , SaveTimer( *io_serv )
56 , CacheFile( cache_file )
59 // load cache from file
60 load_from_cachefile();
63 (void) SaveTimer.expires_from_now( seconds( Config::SaveTimerSeconds ) );
64 SaveTimer.async_wait( bind( &DnsCache::schedule_save, this,
65 boost::asio::placeholders::error ) );
71 GlobalLogger.info() << "Dns Cache is being destructed";
73 // save one last time without re-scheduling the next save
81 void DnsCache::schedule_save(const boost::system::error_code &error)
83 // just in case: ensure SaveTimer is cancelled
84 SaveTimer.cancel(); // (will do nothing if already expired/cancelled)
86 if ( error == boost::asio::error::operation_aborted ) // cancelled
88 GlobalLogger.error() << "DnsCache: SaveTimer was cancelled "
89 << "--> no save and no re-schedule of saving!";
94 GlobalLogger.error() << "DnsCache: Received error " << error
95 << " in schedule_save "
96 << "--> no save now but re-schedule saving";
101 // schedule next save
102 (void) SaveTimer.expires_from_now( seconds( Config::SaveTimerSeconds ) );
103 SaveTimer.async_wait( bind( &DnsCache::schedule_save, this,
104 boost::asio::placeholders::error ) );
107 void DnsCache::save_to_cachefile()
111 GlobalLogger.info() << "DnsCache: skip saving because has not changed";
114 else if (CacheFile.empty())
116 GlobalLogger.warning()
117 << "DnsCache: skip saving because file name empty!";
123 std::ofstream ofs( CacheFile.c_str() );
124 boost::archive::xml_oarchive oa(ofs);
125 oa << boost::serialization::make_nvp("IpCache", IpCache);
126 oa << boost::serialization::make_nvp("CnameCache", CnameCache);
127 GlobalLogger.info() << "DnsCache: saved to cache file " << CacheFile;
131 catch (std::exception &exc)
133 GlobalLogger.warning() << "Saving failed: " << exc.what();
138 void DnsCache::load_from_cachefile()
140 if (CacheFile.empty())
142 GlobalLogger.warning()
143 << "DnsCache: cannot load because cache file name is empty!";
146 else if ( !I2n::file_exists(CacheFile) )
148 GlobalLogger.warning() << "DnsCache: cannot load because cache file "
149 << CacheFile << " does not exist!";
154 HostAddressVec cache;
156 std::ifstream ifs( CacheFile.c_str() );
157 boost::archive::xml_iarchive ia(ifs);
159 ia >> boost::serialization::make_nvp("IpCache", cache);
160 ia >> boost::serialization::make_nvp("CnameCache", cache);
161 GlobalLogger.info() << "DnsCache: loaded from file " << CacheFile;
163 catch (boost::archive::archive_exception &exc)
165 GlobalLogger.warning() << "DnsCache: archive exception loading from "
166 << CacheFile << ": " << exc.what();
168 catch (std::exception &exc)
170 GlobalLogger.warning() << "DnsCache: exception while loading from "
171 << CacheFile << ": " << exc.what();
175 void DnsCache::update(const std::string &hostname,
176 const HostAddressVec &new_data)
178 GlobalLogger.info() << "DnsCache: update IPs for " << hostname
179 << " to " << new_data.size() << "-list";
180 IpCache[hostname] = new_data;
185 void DnsCache::update(const std::string &hostname,
188 GlobalLogger.info() << "DnsCache: update CNAME for " << hostname
189 << " to " << cname.first;
190 CnameCache[hostname] = cname;
195 void DnsCache::update(const std::string &hostname,
196 const uint32_t new_ttl)
198 GlobalLogger.info() << "DnsCache: ensure TTL for IPs for " << hostname
199 << " is below " << new_ttl << "s";
200 HostAddressVec ips = IpCache[hostname];
201 TimeToLive current_ttl;
202 BOOST_FOREACH( HostAddress &addr, ips )
204 current_ttl = addr.get_ttl();
205 if (current_ttl.get_value() > new_ttl)
207 current_ttl.set_value(new_ttl);
208 addr.set_ttl(current_ttl);
212 IpCache[hostname] = ips;
216 HostAddressVec DnsCache::get_ips(const std::string &hostname,
217 const bool check_up_to_date)
219 HostAddressVec result = IpCache[hostname];
220 if (check_up_to_date)
222 HostAddressVec result_up_to_date;
223 int threshold = DnsMaster::get_instance()
224 ->get_resolved_ip_ttl_threshold();
225 BOOST_FOREACH( const HostAddress &addr, result )
227 if (addr.get_ttl().get_updated_value() > threshold)
228 result_up_to_date.push_back(addr);
230 GlobalLogger.debug() << "DnsCache: From cached list of size "
231 << result.size() << " return " << result_up_to_date.size()
232 << " since rest out of date";
233 result = result_up_to_date;
235 GlobalLogger.info() << "DnsCache: request IPs for " << hostname
236 << " --> " << result.size() << "-list";
240 std::string DnsCache::get_cname(const std::string &hostname,
241 const bool check_up_to_date)
243 Cname result_obj = CnameCache[hostname];
244 GlobalLogger.info() << "DnsCache: request CNAME for " << hostname
245 << " --> \"" << result_obj.first << "\"";
246 if (check_up_to_date)
248 if (result_obj.second.get_updated_value() > DnsMaster::get_instance()
249 ->get_resolved_ip_ttl_threshold())
250 return result_obj.first;
253 GlobalLogger.debug() << "DnsCache: Cname is out of date";
258 return result_obj.first;
261 // underlying assumption in this function: for a hostname, the cache has either
262 // a list of IPs saved or a cname saved, but never both
263 HostAddressVec DnsCache::get_ips_recursive(const std::string &hostname,
264 const bool check_up_to_date)
266 std::string current_host = hostname;
267 HostAddressVec result = get_ips(current_host);
268 int n_recursions = 0;
269 while ( result.empty() )
271 current_host = get_cname(current_host, check_up_to_date);
272 if (current_host.empty())
274 else if (++n_recursions >= Config::MaxRetrievalRecursions)
276 GlobalLogger.warning() << "DnsCache: reached recursion limit of "
277 << n_recursions << " in recursive IP retrieval!";
281 result = get_ips(current_host, check_up_to_date);
286 // (created using vim -- the world's best text editor)