finished self-implementation of DNS resolver recursion; will now remove all that!
[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
946356e1 25#include <fstream>
96779587 26#include <logfunc.hpp>
946356e1 27#include <filefunc.hxx> // I2n::file_exists
e18c1337 28#include <boost/foreach.hpp>
96779587
CH
29#include <boost/bind.hpp>
30#include <boost/date_time/posix_time/posix_time.hpp>
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>
96779587 38
dbe986b9
CH
39#include "dns/dnsmaster.h"
40
96779587
CH
41using boost::bind;
42using boost::posix_time::seconds;
43using I2n::Logger::GlobalLogger;
44
45namespace Config
46{
47 int SaveTimerSeconds = 60;
dbe986b9 48 int MaxRetrievalRecursions = 10;
96779587
CH
49}
50
96779587
CH
51DnsCache::DnsCache(const IoServiceItem &io_serv,
52 const std::string &cache_file)
ad83004d
CH
53 : IpCache()
54 , CnameCache()
96779587
CH
55 , SaveTimer( *io_serv )
56 , CacheFile( cache_file )
57 , HasChanged( false )
58{
59 // load cache from file
60 load_from_cachefile();
61
62 // schedule next save
63 (void) SaveTimer.expires_from_now( seconds( Config::SaveTimerSeconds ) );
64 SaveTimer.async_wait( bind( &DnsCache::schedule_save, this,
65 boost::asio::placeholders::error ) );
66}
67
68
69DnsCache::~DnsCache()
70{
e91538f0
CH
71 GlobalLogger.info() << "Dns Cache is being destructed";
72
96779587
CH
73 // save one last time without re-scheduling the next save
74 save_to_cachefile();
75
76 // cancel save timer
77 SaveTimer.cancel();
78}
79
80
81void DnsCache::schedule_save(const boost::system::error_code &error)
82{
83 // just in case: ensure SaveTimer is cancelled
84 SaveTimer.cancel(); // (will do nothing if already expired/cancelled)
85
86 if ( error == boost::asio::error::operation_aborted ) // cancelled
87 {
ad83004d 88 GlobalLogger.error() << "DnsCache: SaveTimer was cancelled "
96779587
CH
89 << "--> no save and no re-schedule of saving!";
90 return;
91 }
92 else if (error)
93 {
ad83004d 94 GlobalLogger.error() << "DnsCache: Received error " << error
96779587
CH
95 << " in schedule_save "
96 << "--> no save now but re-schedule saving";
97 }
98 else
99 save_to_cachefile();
100
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 ) );
105}
106
107void DnsCache::save_to_cachefile()
108{
109 if (!HasChanged)
110 {
ad83004d 111 GlobalLogger.info() << "DnsCache: skip saving because has not changed";
96779587
CH
112 return;
113 }
946356e1
CH
114 else if (CacheFile.empty())
115 {
116 GlobalLogger.warning()
ad83004d 117 << "DnsCache: skip saving because file name empty!";
946356e1
CH
118 return;
119 }
96779587 120
946356e1
CH
121 try
122 {
123 std::ofstream ofs( CacheFile.c_str() );
124 boost::archive::xml_oarchive oa(ofs);
ad83004d
CH
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;
946356e1
CH
128
129 HasChanged = false;
130 }
131 catch (std::exception &exc)
132 {
133 GlobalLogger.warning() << "Saving failed: " << exc.what();
134 }
96779587
CH
135}
136
137
138void DnsCache::load_from_cachefile()
139{
946356e1
CH
140 if (CacheFile.empty())
141 {
142 GlobalLogger.warning()
ad83004d 143 << "DnsCache: cannot load because cache file name is empty!";
946356e1
CH
144 return;
145 }
146 else if ( !I2n::file_exists(CacheFile) )
147 {
ad83004d 148 GlobalLogger.warning() << "DnsCache: cannot load because cache file "
946356e1
CH
149 << CacheFile << " does not exist!";
150 return;
151 }
152 try
153 {
154 HostAddressVec cache;
155
156 std::ifstream ifs( CacheFile.c_str() );
157 boost::archive::xml_iarchive ia(ifs);
158
ad83004d
CH
159 ia >> boost::serialization::make_nvp("IpCache", cache);
160 ia >> boost::serialization::make_nvp("CnameCache", cache);
161 GlobalLogger.info() << "DnsCache: loaded from file " << CacheFile;
946356e1
CH
162 }
163 catch (boost::archive::archive_exception &exc)
164 {
ad83004d 165 GlobalLogger.warning() << "DnsCache: archive exception loading from "
946356e1
CH
166 << CacheFile << ": " << exc.what();
167 }
168 catch (std::exception &exc)
169 {
ad83004d 170 GlobalLogger.warning() << "DnsCache: exception while loading from "
946356e1
CH
171 << CacheFile << ": " << exc.what();
172 }
96779587
CH
173}
174
96779587 175void DnsCache::update(const std::string &hostname,
4e7b6ff9 176 const HostAddressVec &new_data)
96779587 177{
ad83004d 178 GlobalLogger.info() << "DnsCache: update IPs for " << hostname
96779587 179 << " to " << new_data.size() << "-list";
ad83004d 180 IpCache[hostname] = new_data;
96779587
CH
181 HasChanged = true;
182}
183
184
ad83004d 185void DnsCache::update(const std::string &hostname,
dbe986b9 186 const Cname &cname)
ad83004d
CH
187{
188 GlobalLogger.info() << "DnsCache: update CNAME for " << hostname
dbe986b9 189 << " to " << cname.first;
ad83004d
CH
190 CnameCache[hostname] = cname;
191 HasChanged = true;
192}
193
194
dbe986b9 195void DnsCache::update(const std::string &hostname,
ad83004d
CH
196 const uint32_t new_ttl)
197{
198 GlobalLogger.info() << "DnsCache: ensure TTL for IPs for " << hostname
e18c1337 199 << " is below " << new_ttl << "s";
ad83004d 200 HostAddressVec ips = IpCache[hostname];
ad83004d
CH
201 TimeToLive current_ttl;
202 BOOST_FOREACH( HostAddress &addr, ips )
203 {
204 current_ttl = addr.get_ttl();
205 if (current_ttl.get_value() > new_ttl)
206 {
207 current_ttl.set_value(new_ttl);
208 addr.set_ttl(current_ttl);
209 HasChanged = true;
210 }
211 }
e18c1337 212 IpCache[hostname] = ips;
ad83004d
CH
213}
214
215
dbe986b9
CH
216HostAddressVec DnsCache::get_ips(const std::string &hostname,
217 const bool check_up_to_date)
96779587 218{
dbe986b9
CH
219 HostAddressVec result = IpCache[hostname];
220 if (check_up_to_date)
221 {
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 )
226 {
227 if (addr.get_ttl().get_updated_value() > threshold)
228 result_up_to_date.push_back(addr);
229 }
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;
234 }
ad83004d 235 GlobalLogger.info() << "DnsCache: request IPs for " << hostname
dbe986b9
CH
236 << " --> " << result.size() << "-list";
237 return result;
96779587
CH
238}
239
dbe986b9
CH
240std::string DnsCache::get_cname(const std::string &hostname,
241 const bool check_up_to_date)
ad83004d 242{
dbe986b9 243 Cname result_obj = CnameCache[hostname];
ad83004d 244 GlobalLogger.info() << "DnsCache: request CNAME for " << hostname
dbe986b9
CH
245 << " --> \"" << result_obj.first << "\"";
246 if (check_up_to_date)
247 {
248 if (result_obj.second.get_updated_value() > DnsMaster::get_instance()
249 ->get_resolved_ip_ttl_threshold())
250 return result_obj.first;
251 else
252 {
253 GlobalLogger.debug() << "DnsCache: Cname is out of date";
254 return "";
255 }
256 }
257 else
258 return result_obj.first;
ad83004d
CH
259}
260
dbe986b9
CH
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
263HostAddressVec DnsCache::get_ips_recursive(const std::string &hostname,
264 const bool check_up_to_date)
e18c1337
CH
265{
266 std::string current_host = hostname;
dbe986b9
CH
267 HostAddressVec result = get_ips(current_host);
268 int n_recursions = 0;
e18c1337
CH
269 while ( result.empty() )
270 {
dbe986b9 271 current_host = get_cname(current_host, check_up_to_date);
e18c1337
CH
272 if (current_host.empty())
273 break;
dbe986b9
CH
274 else if (++n_recursions >= Config::MaxRetrievalRecursions)
275 {
276 GlobalLogger.warning() << "DnsCache: reached recursion limit of "
277 << n_recursions << " in recursive IP retrieval!";
278 break;
279 }
e18c1337 280 else
dbe986b9 281 result = get_ips(current_host, check_up_to_date);
e18c1337
CH
282 }
283 return result;
284}
ad83004d 285
96779587
CH
286// (created using vim -- the world's best text editor)
287