Write out cache file to temporary file + atomic replace
[pingcheck] / src / dns / dnscache.cpp
CommitLineData
96779587
CH
1/*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
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.
13
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.
16
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.
19
20 Christian Herdtweck, Intra2net AG 2015
21 */
22
c5b4902d 23#include "dns/dnscache.h"
96779587 24
f636ba88 25#include <sstream>
946356e1 26#include <fstream>
96779587 27#include <logfunc.hpp>
946356e1 28#include <filefunc.hxx> // I2n::file_exists
ac012457 29#include <tmpfstream.hpp>
e18c1337 30#include <boost/foreach.hpp>
96779587 31#include <boost/bind.hpp>
96779587 32#include <boost/asio/placeholders.hpp>
946356e1
CH
33#include <boost/serialization/serialization.hpp>
34#include <boost/serialization/map.hpp>
35#include <boost/serialization/string.hpp>
36#include <boost/serialization/vector.hpp>
37#include <boost/archive/xml_oarchive.hpp>
38#include <boost/archive/xml_iarchive.hpp>
c98b5fcb 39#include <boost/date_time/gregorian/gregorian_types.hpp>
96779587 40
dbe986b9
CH
41#include "dns/dnsmaster.h"
42
96779587
CH
43using boost::bind;
44using boost::posix_time::seconds;
45using I2n::Logger::GlobalLogger;
46
8d26221d 47
96779587
CH
48namespace Config
49{
77319ba8 50 const int SAVE_TIMER_SECONDS = 60;
81cf5681 51 const int CACHE_TIME_WARP_THRESH_MINS = 10;
4e691eb0 52 const int CACHE_REMOVE_OUTDATED_DAYS = 60;
96779587
CH
53}
54
8d26221d 55
f833126b
CH
56// -----------------------------------------------------------------------------
57// DNS Cache constructor / destructor
58// -----------------------------------------------------------------------------
59
8d26221d
CH
60const string DnsCache::DoNotUseCacheFile = "do not use cache file!";
61
96779587 62DnsCache::DnsCache(const IoServiceItem &io_serv,
f833126b
CH
63 const std::string &cache_file,
64 const uint32_t min_time_between_resolves)
ad83004d
CH
65 : IpCache()
66 , CnameCache()
96779587
CH
67 , SaveTimer( *io_serv )
68 , CacheFile( cache_file )
69 , HasChanged( false )
f833126b 70 , MinTimeBetweenResolves( min_time_between_resolves )
96779587
CH
71{
72 // load cache from file
73 load_from_cachefile();
74
75 // schedule next save
77319ba8 76 (void) SaveTimer.expires_from_now( seconds( Config::SAVE_TIMER_SECONDS ) );
96779587
CH
77 SaveTimer.async_wait( bind( &DnsCache::schedule_save, this,
78 boost::asio::placeholders::error ) );
79}
80
81
82DnsCache::~DnsCache()
83{
26b0f687 84 GlobalLogger.info() << "DnsCache: being destructed";
e91538f0 85
96779587
CH
86 // save one last time without re-scheduling the next save
87 save_to_cachefile();
88
89 // cancel save timer
90 SaveTimer.cancel();
91}
92
93
f833126b
CH
94// -----------------------------------------------------------------------------
95// LOAD / SAVE
96// -----------------------------------------------------------------------------
97
96779587
CH
98void DnsCache::schedule_save(const boost::system::error_code &error)
99{
96779587
CH
100 if ( error == boost::asio::error::operation_aborted ) // cancelled
101 {
49b82a1d
CH
102 GlobalLogger.info() << "DnsCache: SaveTimer was cancelled "
103 << "--> no save and no re-schedule of saving!";
96779587
CH
104 return;
105 }
106 else if (error)
107 {
49b82a1d
CH
108 GlobalLogger.info() << "DnsCache: Received error " << error
109 << " in schedule_save "
110 << "--> no save now but re-schedule saving";
96779587
CH
111 }
112 else
113 save_to_cachefile();
114
115 // schedule next save
77319ba8 116 (void) SaveTimer.expires_from_now( seconds( Config::SAVE_TIMER_SECONDS ) );
96779587
CH
117 SaveTimer.async_wait( bind( &DnsCache::schedule_save, this,
118 boost::asio::placeholders::error ) );
119}
120
121void DnsCache::save_to_cachefile()
122{
123 if (!HasChanged)
ad83004d 124 GlobalLogger.info() << "DnsCache: skip saving because has not changed";
946356e1 125 else if (CacheFile.empty())
49b82a1d 126 GlobalLogger.info()
ad83004d 127 << "DnsCache: skip saving because file name empty!";
8d26221d 128 else if (CacheFile == DoNotUseCacheFile)
26b0f687
CH
129 GlobalLogger.info() << "DnsCache: configured not to use cache file";
130 else
946356e1 131 {
26b0f687
CH
132 try
133 {
c98b5fcb
CH
134 // clean up: remove very old entries
135 remove_old_entries();
136
77319ba8
CH
137 // remember time of save
138 std::string cache_save_time_str = boost::posix_time::to_iso_string(
139 boost::posix_time::second_clock::universal_time() );
140
ac012457 141 I2n::tmpofcopystream ofs( CacheFile.c_str() );
26b0f687 142 boost::archive::xml_oarchive oa(ofs);
26b0f687
CH
143 oa & BOOST_SERIALIZATION_NVP(IpCache);
144 oa & BOOST_SERIALIZATION_NVP(CnameCache);
77319ba8 145 oa & BOOST_SERIALIZATION_NVP(cache_save_time_str);
8d26221d
CH
146 GlobalLogger.info() << "DnsCache: saved to cache file "
147 << CacheFile;
26b0f687
CH
148
149 HasChanged = false;
150 }
151 catch (std::exception &exc)
152 {
c1abff61 153 GlobalLogger.warning() << "DnsCache: Saving failed: " << exc.what();
26b0f687 154 }
946356e1 155 }
96779587
CH
156}
157
96779587
CH
158void DnsCache::load_from_cachefile()
159{
946356e1 160 if (CacheFile.empty())
49b82a1d 161 GlobalLogger.info()
8d26221d
CH
162 << "DnsCache: cannot load because cache file name is empty!";
163 else if (CacheFile == DoNotUseCacheFile)
26b0f687 164 GlobalLogger.info() << "DnsCache: configured not to use cache file";
946356e1 165 else if ( !I2n::file_exists(CacheFile) )
ad83004d 166 GlobalLogger.warning() << "DnsCache: cannot load because cache file "
946356e1 167 << CacheFile << " does not exist!";
26b0f687 168 else
946356e1 169 {
26b0f687
CH
170 try
171 {
172 std::ifstream ifs( CacheFile.c_str() );
173 boost::archive::xml_iarchive ia(ifs);
946356e1 174
d92e7d02
TJ
175 ip_map_type new_IpCache;
176 cname_map_type new_CnameCache;
77319ba8
CH
177 std::string cache_save_time_str;
178
d92e7d02
TJ
179 ia & BOOST_SERIALIZATION_NVP(new_IpCache);
180 ia & BOOST_SERIALIZATION_NVP(new_CnameCache);
77319ba8
CH
181 ia & BOOST_SERIALIZATION_NVP(cache_save_time_str);
182
d92e7d02 183 const boost::posix_time::ptime cache_save_time
77319ba8 184 = boost::posix_time::from_iso_string(cache_save_time_str);
26b0f687 185 GlobalLogger.info() << "DnsCache: loaded from file " << CacheFile;
77319ba8 186
d92e7d02
TJ
187 // atomic switch over
188 IpCache.swap(new_IpCache);
189 CnameCache.swap(new_CnameCache);
190
77319ba8 191 check_timestamps(cache_save_time);
26b0f687
CH
192 }
193 catch (boost::archive::archive_exception &exc)
194 {
8d26221d
CH
195 GlobalLogger.warning()
196 << "DnsCache: archive exception loading from " << CacheFile
197 << ": " << exc.what();
26b0f687
CH
198 }
199 catch (std::exception &exc)
200 {
201 GlobalLogger.warning() << "DnsCache: exception while loading from "
202 << CacheFile << ": " << exc.what();
203 }
946356e1 204 }
96779587
CH
205}
206
26b0f687 207
77319ba8
CH
208/**
209 * @brief check that loaded cache really is from the past
210 *
211 * Added this to avoid trouble in case the system time is changed into the past.
64a85329 212 * In that case would have TTLs here in cache that are valid much too long.
77319ba8
CH
213 *
214 * Therefore check SaveCacheTime and if that is in the future, set all TTLs to 0
215 *
216 * @returns true if had to re-set timestamps
217 */
218bool DnsCache::check_timestamps(const boost::posix_time::ptime &cache_save_time)
219{
220 // check if CacheSaveTime is in the future
221 boost::posix_time::ptime now
222 = boost::posix_time::second_clock::universal_time()
81cf5681 223 + boost::posix_time::minutes(Config::CACHE_TIME_WARP_THRESH_MINS);
77319ba8
CH
224 if (now > cache_save_time)
225 { // Cache was saved in the past -- everything alright
226 return false;
227 }
228
229 GlobalLogger.warning() << "DnsCache: loaded cache from the future (saved "
230 << cache_save_time << ")! Resetting all TTLs to 0.";
231
232 // reset TTLs in IP cache
81cf5681 233 BOOST_FOREACH( ip_map_type::value_type &key_and_ip, IpCache )
77319ba8 234 {
81cf5681
CH
235 BOOST_FOREACH( HostAddress &address, key_and_ip.second )
236 address.get_ttl().set_value(0);
77319ba8
CH
237 }
238
239 // reset TTLs in CNAME cache
81cf5681
CH
240 BOOST_FOREACH( cname_map_type::value_type &key_and_cname, CnameCache )
241 key_and_cname.second.Ttl.set_value(0);
242
243 HasChanged = true;
244
245 //debug_print();
77319ba8
CH
246
247 return true;
248}
249
250
c98b5fcb
CH
251/**
252 * @brief remove entries from cache that are older than a certain threshold
253 *
254 * this also removes entres from IpCache with no IPs
255 */
256void DnsCache::remove_old_entries()
257{
258 boost::posix_time::ptime thresh
259 = boost::posix_time::second_clock::universal_time()
260 - boost::gregorian::days( Config::CACHE_REMOVE_OUTDATED_DAYS );
261
262 // IP cache
c98b5fcb 263 {
81cf5681
CH
264 ip_map_type::iterator it = IpCache.begin();
265 ip_map_type::iterator it_end = IpCache.end();
266 bool some_ip_up_to_date;
267 while (it != it_end)
c98b5fcb 268 {
81cf5681
CH
269 some_ip_up_to_date = false;
270 BOOST_FOREACH( const HostAddress &address, (*it).second )
c98b5fcb 271 {
81cf5681
CH
272 if ( ! address.get_ttl().was_set_before(thresh) )
273 {
274 some_ip_up_to_date = true;
275 break;
276 }
c98b5fcb 277 }
c98b5fcb 278
81cf5681
CH
279 if ( ! some_ip_up_to_date )
280 {
281 GlobalLogger.debug() << "DnsCache: Removing empty/outdated IP "
282 << "list for " << (*it).first.first;
283 IpCache.erase( (*it++).first );
284 }
285 else
286 ++it;
c98b5fcb
CH
287 }
288 }
289
290 // CNAME cache
c98b5fcb 291 {
81cf5681
CH
292 cname_map_type::iterator it = CnameCache.begin();
293 cname_map_type::iterator it_end = CnameCache.end();
294 while (it != it_end)
c98b5fcb 295 {
81cf5681
CH
296 if ( (*it).second.Ttl.was_set_before( thresh ) )
297 {
298 GlobalLogger.debug() << "DnsCache: Removing outdated CNAME for "
299 << (*it).first;
300 CnameCache.erase( (*it++).first );
301 }
302 else
303 ++it;
c98b5fcb
CH
304 }
305 }
306}
307
f833126b
CH
308// -----------------------------------------------------------------------------
309// UPDATE
310// -----------------------------------------------------------------------------
311
8f00b3df
CH
312/*
313 * warn if hostname is empty and remove trailing dot
314 * also warn if protocol is neither IPv4 nor IPv6
315 */
316ip_map_key_type DnsCache::key_for_ips(const std::string &hostname,
317 const DnsIpProtocol &protocol) const
96779587 318{
26b0f687
CH
319 if (hostname.empty())
320 {
49b82a1d 321 GlobalLogger.info() << "DnsCache: empty host!";
8f00b3df
CH
322 return ip_map_key_type("", DNS_IPALL);
323 }
324 if (protocol == DNS_IPALL)
325 {
326 GlobalLogger.info() << "DnsCache: neither IPv4 nor v6!";
327 return ip_map_key_type("", DNS_IPALL);
26b0f687
CH
328 }
329
330 // check whether last character is a dot
331 if (hostname.rfind('.') == hostname.length()-1)
8f00b3df
CH
332 return ip_map_key_type( hostname.substr(0, hostname.length()-1),
333 protocol );
26b0f687 334 else
8f00b3df
CH
335 return ip_map_key_type( hostname,
336 protocol );
96779587
CH
337}
338
339
ad83004d 340void DnsCache::update(const std::string &hostname,
8f00b3df 341 const DnsIpProtocol &protocol,
26b0f687 342 const HostAddressVec &new_ips)
ad83004d 343{
8f00b3df
CH
344 // check for valid input arguments
345 ip_map_key_type key = key_for_ips(hostname, protocol);
346 if ( key.first.empty() )
347 return;
348
349 // ensure that there is never IP and CNAME for the same host
26b0f687 350 if ( !get_cname(hostname).Host.empty() )
8f00b3df
CH
351 {
352 GlobalLogger.info() << "DnsCache: Saving IPs for " << key.first
26b0f687
CH
353 << " removes CNAME to " << get_cname(hostname).Host << "!";
354 update(hostname, Cname()); // overwrite with "empty" cname
355 }
8f00b3df 356
f833126b
CH
357 // ensure min ttl of MinTimeBetweenResolves
358 HostAddressVec ips_checked;
359 BOOST_FOREACH( const HostAddress &addr, new_ips )
360 {
361 if ( addr.get_ttl().get_value() < MinTimeBetweenResolves )
362 {
363 GlobalLogger.info() << "DnsCache: Correcting TTL of IP for "
8f00b3df 364 << key.first << " from " << addr.get_ttl().get_value() << "s to "
f833126b
CH
365 << MinTimeBetweenResolves << "s because was too short";
366 ips_checked.push_back( HostAddress( addr.get_ip(),
367 MinTimeBetweenResolves) );
368 }
369 else
370 ips_checked.push_back(addr);
371 }
372
8f00b3df 373 // write IPs into one log line
f636ba88 374 stringstream log_temp;
8f00b3df 375 log_temp << "DnsCache: update IPs for " << key.first << " to "
f636ba88
CH
376 << ips_checked.size() << "-list: ";
377 BOOST_FOREACH( const HostAddress &ip, ips_checked )
378 log_temp << ip.get_ip() << ", ";
379 GlobalLogger.notice() << log_temp.str();
f833126b
CH
380
381 IpCache[key] = ips_checked;
ad83004d
CH
382 HasChanged = true;
383}
384
385
8f00b3df
CH
386/*
387 * warn if hostname is empty and remove trailing dot
388 */
389cname_map_key_type DnsCache::key_for_cname(const std::string &hostname) const
390{
391 if (hostname.empty())
392 {
393 GlobalLogger.info() << "DnsCache: empty host!";
394 return "";
395 }
396
397 // check whether last character is a dot
398 if (hostname.rfind('.') == hostname.length()-1)
399 return hostname.substr(0, hostname.length()-1);
400 else
401 return hostname;
402}
403
404
dbe986b9 405void DnsCache::update(const std::string &hostname,
26b0f687 406 const Cname &cname)
ad83004d 407{
8f00b3df
CH
408 // check for valid input arguments
409 cname_map_key_type key = key_for_cname(hostname);
410 if ( key.empty() )
411 return;
412
413 // ensure that there is never IP and CNAME for the same host
414 int n_ips = get_ips(hostname, DNS_IPv4).size()
415 + get_ips(hostname, DNS_IPv6).size();
416 if ( n_ips > 0 )
417 {
418 GlobalLogger.info() << "DnsCache: Saving IPs for " << key
419 << " removes CNAME to " << get_cname(hostname).Host << "!";
49b82a1d 420 GlobalLogger.info() << "DnsCache: Saving CNAME for " << key
8f00b3df
CH
421 << " removes " << n_ips << " IPs for same host!";
422 update(hostname, DNS_IPv4, HostAddressVec());
423 update(hostname, DNS_IPv6, HostAddressVec());
ad83004d 424 }
26b0f687 425
8f00b3df
CH
426 // remove possible trailing dot from cname's target host
427 Cname to_save = Cname(key_for_cname(cname.Host), // implicit cast to string
26b0f687
CH
428 cname.Ttl);
429
f833126b
CH
430 // ensure min ttl of MinTimeBetweenResolves
431 if ( to_save.Ttl.get_value() < MinTimeBetweenResolves )
432 {
433 GlobalLogger.info() << "DnsCache: Correcting TTL of CNAME of "
8f00b3df 434 << key << " from " << to_save.Ttl.get_value() << "s to "
f833126b
CH
435 << MinTimeBetweenResolves << "s because was too short";
436 to_save.Ttl = TimeToLive(MinTimeBetweenResolves);
437 }
438
f636ba88 439 GlobalLogger.notice() << "DnsCache: update CNAME for " << key
8f00b3df 440 << " to " << to_save.Host;
26b0f687
CH
441 CnameCache[key] = to_save;
442 HasChanged = true;
ad83004d
CH
443}
444
445
f833126b
CH
446// -----------------------------------------------------------------------------
447// RETRIEVAL
448// -----------------------------------------------------------------------------
449
23f51766
CH
450/**
451 * @returns empty list if no (up to date) ips for hostname in cache
452 */
dbe986b9 453HostAddressVec DnsCache::get_ips(const std::string &hostname,
8f00b3df 454 const DnsIpProtocol &protocol,
dbe986b9 455 const bool check_up_to_date)
96779587 456{
8f00b3df 457 ip_map_key_type key = key_for_ips(hostname, protocol);
26b0f687 458 HostAddressVec result = IpCache[key];
dbe986b9
CH
459 if (check_up_to_date)
460 {
461 HostAddressVec result_up_to_date;
23f51766
CH
462 uint32_t threshold = static_cast<uint32_t>(
463 DnsMaster::get_instance()->get_resolved_ip_ttl_threshold() );
26b0f687 464 uint32_t updated_ttl;
dbe986b9
CH
465 BOOST_FOREACH( const HostAddress &addr, result )
466 {
26b0f687
CH
467 updated_ttl = addr.get_ttl().get_updated_value();
468 if (updated_ttl > threshold)
dbe986b9 469 result_up_to_date.push_back(addr);
26b0f687
CH
470 else
471 GlobalLogger.debug() << "DnsCache: do not return "
472 << addr.get_ip().to_string() << " since TTL "
473 << updated_ttl << "s is out of date (thresh="
474 << threshold << "s)";
dbe986b9 475 }
dbe986b9
CH
476 result = result_up_to_date;
477 }
8f00b3df 478 /*GlobalLogger.debug() << "DnsCache: request IPs for " << key.first
26b0f687
CH
479 << " --> " << result.size() << "-list";
480 BOOST_FOREACH( const HostAddress &addr, result )
481 GlobalLogger.debug() << "DnsCache: " << addr.get_ip().to_string()
fd62d09f 482 << " (TTL " << addr.get_ttl().get_updated_value()
2a4dde8b 483 << "s)"; */
dbe986b9 484 return result;
96779587
CH
485}
486
23f51766
CH
487/**
488 * @returns empty cname if no (up to date cname) for hostname in cache
489 */
490Cname DnsCache::get_cname(const std::string &hostname,
491 const bool check_up_to_date)
ad83004d 492{
8f00b3df 493 cname_map_key_type key = key_for_cname(hostname);
26b0f687 494 Cname result_obj = CnameCache[key];
2a4dde8b 495 /*GlobalLogger.debug() << "DnsCache: request CNAME for " << key
fd62d09f 496 << " --> \"" << result_obj.Host << "\" (TTL "
2a4dde8b 497 << result_obj.Ttl.get_updated_value() << "s)";*/
23f51766
CH
498 if (result_obj.Host.empty())
499 return result_obj;
500
501 else if (check_up_to_date)
dbe986b9 502 {
26b0f687
CH
503 if ( result_obj.Ttl.get_updated_value() > static_cast<uint32_t>(
504 DnsMaster::get_instance()->get_resolved_ip_ttl_threshold()) )
23f51766 505 return result_obj;
dbe986b9
CH
506 else
507 {
23f51766
CH
508 GlobalLogger.debug() << "DnsCache: CNAME is out of date";
509 return Cname(); // same as if there was no cname for hostname
dbe986b9
CH
510 }
511 }
512 else
23f51766 513 return result_obj;
ad83004d
CH
514}
515
dbe986b9
CH
516// underlying assumption in this function: for a hostname, the cache has either
517// a list of IPs saved or a cname saved, but never both
518HostAddressVec DnsCache::get_ips_recursive(const std::string &hostname,
8f00b3df 519 const DnsIpProtocol &protocol,
dbe986b9 520 const bool check_up_to_date)
e18c1337
CH
521{
522 std::string current_host = hostname;
23f51766 523 Cname current_cname;
8f00b3df 524 HostAddressVec result = get_ips(current_host, protocol, check_up_to_date);
dbe986b9 525 int n_recursions = 0;
23f51766 526 uint32_t min_cname_ttl = 0xffff; // largest possible unsigned 4-byte value
cd71d095
CH
527 int max_recursion_count = DnsMaster::get_instance()
528 ->get_max_recursion_count();
e18c1337
CH
529 while ( result.empty() )
530 {
23f51766 531 current_cname = get_cname(current_host, check_up_to_date);
8d26221d 532 if (current_cname.Host.empty())
8f00b3df
CH
533 break; // no ips (since result.empty()) and no cname
534 // --> will return empty result
8d26221d 535
8f00b3df 536 current_host = current_cname.Host;
cd71d095 537 if (++n_recursions >= max_recursion_count)
dbe986b9 538 {
49b82a1d
CH
539 GlobalLogger.info() << "DnsCache: reached recursion limit of "
540 << n_recursions << " in recursive IP retrieval of "
541 << hostname << "!";
dbe986b9
CH
542 break;
543 }
e18c1337 544 else
23f51766 545 {
fd62d09f
CH
546 min_cname_ttl = min(min_cname_ttl,
547 current_cname.Ttl.get_updated_value());
8f00b3df 548 result = get_ips(current_host, protocol, check_up_to_date);
23f51766
CH
549 }
550 }
551
552 GlobalLogger.debug() << "DnsCache: recursive IP retrieval resulted in "
553 << result.size() << "-list after " << n_recursions
554 << " recursions";
555
556 // adjust ttl to min of ttl and min_cname_ttl
557 if (n_recursions > 0)
558 {
559 TimeToLive cname_ttl(min_cname_ttl);
560
561 BOOST_FOREACH( HostAddress &addr, result )
562 {
fd62d09f 563 if (addr.get_ttl().get_updated_value() > min_cname_ttl)
8d26221d 564 {
2a4dde8b 565 //GlobalLogger.debug() << "DnsCache: using shorter CNAME TTL";
23f51766 566 addr.set_ttl(cname_ttl);
8d26221d 567 }
23f51766 568 }
e18c1337 569 }
23f51766 570
e18c1337
CH
571 return result;
572}
ad83004d 573
23f51766 574/**
9d1b2726
CH
575 * from a list of CNAMEs find the first one that is out of date or empty
576 *
577 * returns the hostname that is out of date or empty if all CNAMEs are
578 * up-to-date
23f51766
CH
579 *
580 * required in ResolverBase::get_skipper
23f51766
CH
581 */
582std::string DnsCache::get_first_outdated_cname(const std::string &hostname,
583 const uint32_t ttl_thresh)
584{
8d26221d 585 std::string first_outdated = hostname;
23f51766
CH
586 Cname cname;
587 int n_recursions = 0;
cd71d095
CH
588 int max_recursion_count = DnsMaster::get_instance()
589 ->get_max_recursion_count();
23f51766
CH
590 while (true)
591 {
cd71d095 592 if (++n_recursions >= max_recursion_count)
23f51766 593 {
49b82a1d
CH
594 GlobalLogger.info() << "DnsCache: reached recursion limit of "
595 << n_recursions << " in search of outdated CNAMEs for "
596 << hostname << "!";
9d1b2726
CH
597 return first_outdated; // not really out of date but currently
598 } // our best guess
23f51766 599
8d26221d
CH
600 cname = get_cname(first_outdated);
601 if (cname.Host.empty())
9d1b2726
CH
602 // reached end of cname list --> everything was up-to-date
603 return "";
23f51766
CH
604 else if (cname.Ttl.get_updated_value() > ttl_thresh)
605 // cname is up to date --> continue looking
8d26221d 606 first_outdated = cname.Host;
23f51766 607 else
9d1b2726
CH
608 // cname is out of date --> return its target
609 return cname.Host;
23f51766 610 }
9d1b2726
CH
611 // reach this point only if cname chain does not end with an IP
612 // --> all are up-to-date
613 return "";
23f51766 614}
e638894d
CH
615
616std::string DnsCache::get_cname_chain_str(const std::string &hostname)
617{
618 std::stringstream temp;
619 temp << hostname;
620 std::string current_host = hostname;
621 Cname current_cname;
622 int n_recursions = 0;
623 int max_recursion_count = DnsMaster::get_instance()
624 ->get_max_recursion_count();
625 while (true)
626 {
627 if (n_recursions >= max_recursion_count)
628 {
629 temp << "...";
630 break;
631 }
632
633 current_cname = get_cname(current_host, false);
634 if (current_cname.Host.empty())
635 break;
636 else
637 {
638 current_host = current_cname.Host;
639 temp << "-->" << current_host;
a176d17a 640 ++n_recursions;
e638894d
CH
641 }
642 }
643 return temp.str();
644}
81cf5681
CH
645
646
647// -----------------------------------------------------------------------------
648// OTHER
649// -----------------------------------------------------------------------------
650void DnsCache::debug_print() const
651{
652 GlobalLogger.debug() << "DnsCache: IP Cache contents:";
653 stringstream log_temp;
654 BOOST_FOREACH( const ip_map_type::value_type &key_and_ip, IpCache )
655 {
656 // write IPs into one log line
657 log_temp.str("");
658 log_temp << "DnsCache: " << key_and_ip.first.first << ": \t "
659 << key_and_ip.second.size() << "-list ";
660 BOOST_FOREACH( const HostAddress &ip, key_and_ip.second )
661 log_temp << ip.get_ip() << "+" << ip.get_ttl().get_updated_value()
662 << "s, ";
663 GlobalLogger.debug() << log_temp.str();
664 }
665
666 GlobalLogger.debug() << "DnsCache: CNAME Cache contents:";
667 BOOST_FOREACH( const cname_map_type::value_type &key_and_cname, CnameCache )
668 GlobalLogger.debug() << "DnsCache: " << key_and_cname.first << ": \t "
669 << key_and_cname.second.Host << "+"
670 << key_and_cname.second.Ttl.get_updated_value()
671 << "s";
672}