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