bugfixed erase and re-set of TTLs; changed time warp thresh from 24h to 10mins
[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
77319ba8
CH
174 std::string cache_save_time_str;
175
26b0f687
CH
176 ia & BOOST_SERIALIZATION_NVP(IpCache);
177 ia & BOOST_SERIALIZATION_NVP(CnameCache);
77319ba8
CH
178 ia & BOOST_SERIALIZATION_NVP(cache_save_time_str);
179
180 boost::posix_time::ptime cache_save_time
181 = boost::posix_time::from_iso_string(cache_save_time_str);
26b0f687 182 GlobalLogger.info() << "DnsCache: loaded from file " << CacheFile;
77319ba8
CH
183
184 check_timestamps(cache_save_time);
26b0f687
CH
185 }
186 catch (boost::archive::archive_exception &exc)
187 {
8d26221d
CH
188 GlobalLogger.warning()
189 << "DnsCache: archive exception loading from " << CacheFile
190 << ": " << exc.what();
26b0f687
CH
191 }
192 catch (std::exception &exc)
193 {
194 GlobalLogger.warning() << "DnsCache: exception while loading from "
195 << CacheFile << ": " << exc.what();
196 }
946356e1 197 }
96779587
CH
198}
199
26b0f687 200
77319ba8
CH
201/**
202 * @brief check that loaded cache really is from the past
203 *
204 * Added this to avoid trouble in case the system time is changed into the past.
205 * In that cast would have TTLs here in cache that are valid much too long.
206 *
207 * Therefore check SaveCacheTime and if that is in the future, set all TTLs to 0
208 *
209 * @returns true if had to re-set timestamps
210 */
211bool DnsCache::check_timestamps(const boost::posix_time::ptime &cache_save_time)
212{
213 // check if CacheSaveTime is in the future
214 boost::posix_time::ptime now
215 = boost::posix_time::second_clock::universal_time()
81cf5681 216 + boost::posix_time::minutes(Config::CACHE_TIME_WARP_THRESH_MINS);
77319ba8
CH
217 if (now > cache_save_time)
218 { // Cache was saved in the past -- everything alright
219 return false;
220 }
221
222 GlobalLogger.warning() << "DnsCache: loaded cache from the future (saved "
223 << cache_save_time << ")! Resetting all TTLs to 0.";
224
225 // reset TTLs in IP cache
81cf5681 226 BOOST_FOREACH( ip_map_type::value_type &key_and_ip, IpCache )
77319ba8 227 {
81cf5681
CH
228 BOOST_FOREACH( HostAddress &address, key_and_ip.second )
229 address.get_ttl().set_value(0);
77319ba8
CH
230 }
231
232 // reset TTLs in CNAME cache
81cf5681
CH
233 BOOST_FOREACH( cname_map_type::value_type &key_and_cname, CnameCache )
234 key_and_cname.second.Ttl.set_value(0);
235
236 HasChanged = true;
237
238 //debug_print();
77319ba8
CH
239
240 return true;
241}
242
243
c98b5fcb
CH
244/**
245 * @brief remove entries from cache that are older than a certain threshold
246 *
247 * this also removes entres from IpCache with no IPs
248 */
249void DnsCache::remove_old_entries()
250{
251 boost::posix_time::ptime thresh
252 = boost::posix_time::second_clock::universal_time()
253 - boost::gregorian::days( Config::CACHE_REMOVE_OUTDATED_DAYS );
254
255 // IP cache
c98b5fcb 256 {
81cf5681
CH
257 ip_map_type::iterator it = IpCache.begin();
258 ip_map_type::iterator it_end = IpCache.end();
259 bool some_ip_up_to_date;
260 while (it != it_end)
c98b5fcb 261 {
81cf5681
CH
262 some_ip_up_to_date = false;
263 BOOST_FOREACH( const HostAddress &address, (*it).second )
c98b5fcb 264 {
81cf5681
CH
265 if ( ! address.get_ttl().was_set_before(thresh) )
266 {
267 some_ip_up_to_date = true;
268 break;
269 }
c98b5fcb 270 }
c98b5fcb 271
81cf5681
CH
272 if ( ! some_ip_up_to_date )
273 {
274 GlobalLogger.debug() << "DnsCache: Removing empty/outdated IP "
275 << "list for " << (*it).first.first;
276 IpCache.erase( (*it++).first );
277 }
278 else
279 ++it;
c98b5fcb
CH
280 }
281 }
282
283 // CNAME cache
c98b5fcb 284 {
81cf5681
CH
285 cname_map_type::iterator it = CnameCache.begin();
286 cname_map_type::iterator it_end = CnameCache.end();
287 while (it != it_end)
c98b5fcb 288 {
81cf5681
CH
289 if ( (*it).second.Ttl.was_set_before( thresh ) )
290 {
291 GlobalLogger.debug() << "DnsCache: Removing outdated CNAME for "
292 << (*it).first;
293 CnameCache.erase( (*it++).first );
294 }
295 else
296 ++it;
c98b5fcb
CH
297 }
298 }
299}
300
f833126b
CH
301// -----------------------------------------------------------------------------
302// UPDATE
303// -----------------------------------------------------------------------------
304
8f00b3df
CH
305/*
306 * warn if hostname is empty and remove trailing dot
307 * also warn if protocol is neither IPv4 nor IPv6
308 */
309ip_map_key_type DnsCache::key_for_ips(const std::string &hostname,
310 const DnsIpProtocol &protocol) const
96779587 311{
26b0f687
CH
312 if (hostname.empty())
313 {
49b82a1d 314 GlobalLogger.info() << "DnsCache: empty host!";
8f00b3df
CH
315 return ip_map_key_type("", DNS_IPALL);
316 }
317 if (protocol == DNS_IPALL)
318 {
319 GlobalLogger.info() << "DnsCache: neither IPv4 nor v6!";
320 return ip_map_key_type("", DNS_IPALL);
26b0f687
CH
321 }
322
323 // check whether last character is a dot
324 if (hostname.rfind('.') == hostname.length()-1)
8f00b3df
CH
325 return ip_map_key_type( hostname.substr(0, hostname.length()-1),
326 protocol );
26b0f687 327 else
8f00b3df
CH
328 return ip_map_key_type( hostname,
329 protocol );
96779587
CH
330}
331
332
ad83004d 333void DnsCache::update(const std::string &hostname,
8f00b3df 334 const DnsIpProtocol &protocol,
26b0f687 335 const HostAddressVec &new_ips)
ad83004d 336{
8f00b3df
CH
337 // check for valid input arguments
338 ip_map_key_type key = key_for_ips(hostname, protocol);
339 if ( key.first.empty() )
340 return;
341
342 // ensure that there is never IP and CNAME for the same host
26b0f687 343 if ( !get_cname(hostname).Host.empty() )
8f00b3df
CH
344 {
345 GlobalLogger.info() << "DnsCache: Saving IPs for " << key.first
26b0f687
CH
346 << " removes CNAME to " << get_cname(hostname).Host << "!";
347 update(hostname, Cname()); // overwrite with "empty" cname
348 }
8f00b3df 349
f833126b
CH
350 // ensure min ttl of MinTimeBetweenResolves
351 HostAddressVec ips_checked;
352 BOOST_FOREACH( const HostAddress &addr, new_ips )
353 {
354 if ( addr.get_ttl().get_value() < MinTimeBetweenResolves )
355 {
356 GlobalLogger.info() << "DnsCache: Correcting TTL of IP for "
8f00b3df 357 << key.first << " from " << addr.get_ttl().get_value() << "s to "
f833126b
CH
358 << MinTimeBetweenResolves << "s because was too short";
359 ips_checked.push_back( HostAddress( addr.get_ip(),
360 MinTimeBetweenResolves) );
361 }
362 else
363 ips_checked.push_back(addr);
364 }
365
8f00b3df 366 // write IPs into one log line
f636ba88 367 stringstream log_temp;
8f00b3df 368 log_temp << "DnsCache: update IPs for " << key.first << " to "
f636ba88
CH
369 << ips_checked.size() << "-list: ";
370 BOOST_FOREACH( const HostAddress &ip, ips_checked )
371 log_temp << ip.get_ip() << ", ";
372 GlobalLogger.notice() << log_temp.str();
f833126b
CH
373
374 IpCache[key] = ips_checked;
ad83004d
CH
375 HasChanged = true;
376}
377
378
8f00b3df
CH
379/*
380 * warn if hostname is empty and remove trailing dot
381 */
382cname_map_key_type DnsCache::key_for_cname(const std::string &hostname) const
383{
384 if (hostname.empty())
385 {
386 GlobalLogger.info() << "DnsCache: empty host!";
387 return "";
388 }
389
390 // check whether last character is a dot
391 if (hostname.rfind('.') == hostname.length()-1)
392 return hostname.substr(0, hostname.length()-1);
393 else
394 return hostname;
395}
396
397
dbe986b9 398void DnsCache::update(const std::string &hostname,
26b0f687 399 const Cname &cname)
ad83004d 400{
8f00b3df
CH
401 // check for valid input arguments
402 cname_map_key_type key = key_for_cname(hostname);
403 if ( key.empty() )
404 return;
405
406 // ensure that there is never IP and CNAME for the same host
407 int n_ips = get_ips(hostname, DNS_IPv4).size()
408 + get_ips(hostname, DNS_IPv6).size();
409 if ( n_ips > 0 )
410 {
411 GlobalLogger.info() << "DnsCache: Saving IPs for " << key
412 << " removes CNAME to " << get_cname(hostname).Host << "!";
49b82a1d 413 GlobalLogger.info() << "DnsCache: Saving CNAME for " << key
8f00b3df
CH
414 << " removes " << n_ips << " IPs for same host!";
415 update(hostname, DNS_IPv4, HostAddressVec());
416 update(hostname, DNS_IPv6, HostAddressVec());
ad83004d 417 }
26b0f687 418
8f00b3df
CH
419 // remove possible trailing dot from cname's target host
420 Cname to_save = Cname(key_for_cname(cname.Host), // implicit cast to string
26b0f687
CH
421 cname.Ttl);
422
f833126b
CH
423 // ensure min ttl of MinTimeBetweenResolves
424 if ( to_save.Ttl.get_value() < MinTimeBetweenResolves )
425 {
426 GlobalLogger.info() << "DnsCache: Correcting TTL of CNAME of "
8f00b3df 427 << key << " from " << to_save.Ttl.get_value() << "s to "
f833126b
CH
428 << MinTimeBetweenResolves << "s because was too short";
429 to_save.Ttl = TimeToLive(MinTimeBetweenResolves);
430 }
431
f636ba88 432 GlobalLogger.notice() << "DnsCache: update CNAME for " << key
8f00b3df 433 << " to " << to_save.Host;
26b0f687
CH
434 CnameCache[key] = to_save;
435 HasChanged = true;
ad83004d
CH
436}
437
438
f833126b
CH
439// -----------------------------------------------------------------------------
440// RETRIEVAL
441// -----------------------------------------------------------------------------
442
23f51766
CH
443/**
444 * @returns empty list if no (up to date) ips for hostname in cache
445 */
dbe986b9 446HostAddressVec DnsCache::get_ips(const std::string &hostname,
8f00b3df 447 const DnsIpProtocol &protocol,
dbe986b9 448 const bool check_up_to_date)
96779587 449{
8f00b3df 450 ip_map_key_type key = key_for_ips(hostname, protocol);
26b0f687 451 HostAddressVec result = IpCache[key];
dbe986b9
CH
452 if (check_up_to_date)
453 {
454 HostAddressVec result_up_to_date;
23f51766
CH
455 uint32_t threshold = static_cast<uint32_t>(
456 DnsMaster::get_instance()->get_resolved_ip_ttl_threshold() );
26b0f687 457 uint32_t updated_ttl;
dbe986b9
CH
458 BOOST_FOREACH( const HostAddress &addr, result )
459 {
26b0f687
CH
460 updated_ttl = addr.get_ttl().get_updated_value();
461 if (updated_ttl > threshold)
dbe986b9 462 result_up_to_date.push_back(addr);
26b0f687
CH
463 else
464 GlobalLogger.debug() << "DnsCache: do not return "
465 << addr.get_ip().to_string() << " since TTL "
466 << updated_ttl << "s is out of date (thresh="
467 << threshold << "s)";
dbe986b9 468 }
dbe986b9
CH
469 result = result_up_to_date;
470 }
8f00b3df 471 /*GlobalLogger.debug() << "DnsCache: request IPs for " << key.first
26b0f687
CH
472 << " --> " << result.size() << "-list";
473 BOOST_FOREACH( const HostAddress &addr, result )
474 GlobalLogger.debug() << "DnsCache: " << addr.get_ip().to_string()
fd62d09f 475 << " (TTL " << addr.get_ttl().get_updated_value()
2a4dde8b 476 << "s)"; */
dbe986b9 477 return result;
96779587
CH
478}
479
23f51766
CH
480/**
481 * @returns empty cname if no (up to date cname) for hostname in cache
482 */
483Cname DnsCache::get_cname(const std::string &hostname,
484 const bool check_up_to_date)
ad83004d 485{
8f00b3df 486 cname_map_key_type key = key_for_cname(hostname);
26b0f687 487 Cname result_obj = CnameCache[key];
2a4dde8b 488 /*GlobalLogger.debug() << "DnsCache: request CNAME for " << key
fd62d09f 489 << " --> \"" << result_obj.Host << "\" (TTL "
2a4dde8b 490 << result_obj.Ttl.get_updated_value() << "s)";*/
23f51766
CH
491 if (result_obj.Host.empty())
492 return result_obj;
493
494 else if (check_up_to_date)
dbe986b9 495 {
26b0f687
CH
496 if ( result_obj.Ttl.get_updated_value() > static_cast<uint32_t>(
497 DnsMaster::get_instance()->get_resolved_ip_ttl_threshold()) )
23f51766 498 return result_obj;
dbe986b9
CH
499 else
500 {
23f51766
CH
501 GlobalLogger.debug() << "DnsCache: CNAME is out of date";
502 return Cname(); // same as if there was no cname for hostname
dbe986b9
CH
503 }
504 }
505 else
23f51766 506 return result_obj;
ad83004d
CH
507}
508
dbe986b9
CH
509// underlying assumption in this function: for a hostname, the cache has either
510// a list of IPs saved or a cname saved, but never both
511HostAddressVec DnsCache::get_ips_recursive(const std::string &hostname,
8f00b3df 512 const DnsIpProtocol &protocol,
dbe986b9 513 const bool check_up_to_date)
e18c1337
CH
514{
515 std::string current_host = hostname;
23f51766 516 Cname current_cname;
8f00b3df 517 HostAddressVec result = get_ips(current_host, protocol, check_up_to_date);
dbe986b9 518 int n_recursions = 0;
23f51766 519 uint32_t min_cname_ttl = 0xffff; // largest possible unsigned 4-byte value
cd71d095
CH
520 int max_recursion_count = DnsMaster::get_instance()
521 ->get_max_recursion_count();
e18c1337
CH
522 while ( result.empty() )
523 {
23f51766 524 current_cname = get_cname(current_host, check_up_to_date);
8d26221d 525 if (current_cname.Host.empty())
8f00b3df
CH
526 break; // no ips (since result.empty()) and no cname
527 // --> will return empty result
8d26221d 528
8f00b3df 529 current_host = current_cname.Host;
cd71d095 530 if (++n_recursions >= max_recursion_count)
dbe986b9 531 {
49b82a1d
CH
532 GlobalLogger.info() << "DnsCache: reached recursion limit of "
533 << n_recursions << " in recursive IP retrieval of "
534 << hostname << "!";
dbe986b9
CH
535 break;
536 }
e18c1337 537 else
23f51766 538 {
fd62d09f
CH
539 min_cname_ttl = min(min_cname_ttl,
540 current_cname.Ttl.get_updated_value());
8f00b3df 541 result = get_ips(current_host, protocol, check_up_to_date);
23f51766
CH
542 }
543 }
544
545 GlobalLogger.debug() << "DnsCache: recursive IP retrieval resulted in "
546 << result.size() << "-list after " << n_recursions
547 << " recursions";
548
549 // adjust ttl to min of ttl and min_cname_ttl
550 if (n_recursions > 0)
551 {
552 TimeToLive cname_ttl(min_cname_ttl);
553
554 BOOST_FOREACH( HostAddress &addr, result )
555 {
fd62d09f 556 if (addr.get_ttl().get_updated_value() > min_cname_ttl)
8d26221d 557 {
2a4dde8b 558 //GlobalLogger.debug() << "DnsCache: using shorter CNAME TTL";
23f51766 559 addr.set_ttl(cname_ttl);
8d26221d 560 }
23f51766 561 }
e18c1337 562 }
23f51766 563
e18c1337
CH
564 return result;
565}
ad83004d 566
23f51766 567/**
9d1b2726
CH
568 * from a list of CNAMEs find the first one that is out of date or empty
569 *
570 * returns the hostname that is out of date or empty if all CNAMEs are
571 * up-to-date
23f51766
CH
572 *
573 * required in ResolverBase::get_skipper
23f51766
CH
574 */
575std::string DnsCache::get_first_outdated_cname(const std::string &hostname,
576 const uint32_t ttl_thresh)
577{
8d26221d 578 std::string first_outdated = hostname;
23f51766
CH
579 Cname cname;
580 int n_recursions = 0;
cd71d095
CH
581 int max_recursion_count = DnsMaster::get_instance()
582 ->get_max_recursion_count();
23f51766
CH
583 while (true)
584 {
cd71d095 585 if (++n_recursions >= max_recursion_count)
23f51766 586 {
49b82a1d
CH
587 GlobalLogger.info() << "DnsCache: reached recursion limit of "
588 << n_recursions << " in search of outdated CNAMEs for "
589 << hostname << "!";
9d1b2726
CH
590 return first_outdated; // not really out of date but currently
591 } // our best guess
23f51766 592
8d26221d
CH
593 cname = get_cname(first_outdated);
594 if (cname.Host.empty())
9d1b2726
CH
595 // reached end of cname list --> everything was up-to-date
596 return "";
23f51766
CH
597 else if (cname.Ttl.get_updated_value() > ttl_thresh)
598 // cname is up to date --> continue looking
8d26221d 599 first_outdated = cname.Host;
23f51766 600 else
9d1b2726
CH
601 // cname is out of date --> return its target
602 return cname.Host;
23f51766 603 }
9d1b2726
CH
604 // reach this point only if cname chain does not end with an IP
605 // --> all are up-to-date
606 return "";
23f51766 607}
e638894d
CH
608
609std::string DnsCache::get_cname_chain_str(const std::string &hostname)
610{
611 std::stringstream temp;
612 temp << hostname;
613 std::string current_host = hostname;
614 Cname current_cname;
615 int n_recursions = 0;
616 int max_recursion_count = DnsMaster::get_instance()
617 ->get_max_recursion_count();
618 while (true)
619 {
620 if (n_recursions >= max_recursion_count)
621 {
622 temp << "...";
623 break;
624 }
625
626 current_cname = get_cname(current_host, false);
627 if (current_cname.Host.empty())
628 break;
629 else
630 {
631 current_host = current_cname.Host;
632 temp << "-->" << current_host;
a176d17a 633 ++n_recursions;
e638894d
CH
634 }
635 }
636 return temp.str();
637}
81cf5681
CH
638
639
640// -----------------------------------------------------------------------------
641// OTHER
642// -----------------------------------------------------------------------------
643void DnsCache::debug_print() const
644{
645 GlobalLogger.debug() << "DnsCache: IP Cache contents:";
646 stringstream log_temp;
647 BOOST_FOREACH( const ip_map_type::value_type &key_and_ip, IpCache )
648 {
649 // write IPs into one log line
650 log_temp.str("");
651 log_temp << "DnsCache: " << key_and_ip.first.first << ": \t "
652 << key_and_ip.second.size() << "-list ";
653 BOOST_FOREACH( const HostAddress &ip, key_and_ip.second )
654 log_temp << ip.get_ip() << "+" << ip.get_ttl().get_updated_value()
655 << "s, ";
656 GlobalLogger.debug() << log_temp.str();
657 }
658
659 GlobalLogger.debug() << "DnsCache: CNAME Cache contents:";
660 BOOST_FOREACH( const cname_map_type::value_type &key_and_cname, CnameCache )
661 GlobalLogger.debug() << "DnsCache: " << key_and_cname.first << ": \t "
662 << key_and_cname.second.Host << "+"
663 << key_and_cname.second.Ttl.get_updated_value()
664 << "s";
665}