continue implementation; first tests with recursion returned IPs but then added cance...
[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
CH
38
39using boost::bind;
40using boost::posix_time::seconds;
41using I2n::Logger::GlobalLogger;
42
43namespace Config
44{
45 int SaveTimerSeconds = 60;
46}
47
48
49
50DnsCache::DnsCache(const IoServiceItem &io_serv,
51 const std::string &cache_file)
ad83004d
CH
52 : IpCache()
53 , CnameCache()
96779587
CH
54 , SaveTimer( *io_serv )
55 , CacheFile( cache_file )
56 , HasChanged( false )
57{
58 // load cache from file
59 load_from_cachefile();
60
61 // schedule next save
62 (void) SaveTimer.expires_from_now( seconds( Config::SaveTimerSeconds ) );
63 SaveTimer.async_wait( bind( &DnsCache::schedule_save, this,
64 boost::asio::placeholders::error ) );
65}
66
67
68DnsCache::~DnsCache()
69{
e91538f0
CH
70 GlobalLogger.info() << "Dns Cache is being destructed";
71
96779587
CH
72 // save one last time without re-scheduling the next save
73 save_to_cachefile();
74
75 // cancel save timer
76 SaveTimer.cancel();
77}
78
79
80void DnsCache::schedule_save(const boost::system::error_code &error)
81{
82 // just in case: ensure SaveTimer is cancelled
83 SaveTimer.cancel(); // (will do nothing if already expired/cancelled)
84
85 if ( error == boost::asio::error::operation_aborted ) // cancelled
86 {
ad83004d 87 GlobalLogger.error() << "DnsCache: SaveTimer was cancelled "
96779587
CH
88 << "--> no save and no re-schedule of saving!";
89 return;
90 }
91 else if (error)
92 {
ad83004d 93 GlobalLogger.error() << "DnsCache: Received error " << error
96779587
CH
94 << " in schedule_save "
95 << "--> no save now but re-schedule saving";
96 }
97 else
98 save_to_cachefile();
99
100 // schedule next save
101 (void) SaveTimer.expires_from_now( seconds( Config::SaveTimerSeconds ) );
102 SaveTimer.async_wait( bind( &DnsCache::schedule_save, this,
103 boost::asio::placeholders::error ) );
104}
105
106void DnsCache::save_to_cachefile()
107{
108 if (!HasChanged)
109 {
ad83004d 110 GlobalLogger.info() << "DnsCache: skip saving because has not changed";
96779587
CH
111 return;
112 }
946356e1
CH
113 else if (CacheFile.empty())
114 {
115 GlobalLogger.warning()
ad83004d 116 << "DnsCache: skip saving because file name empty!";
946356e1
CH
117 return;
118 }
96779587 119
946356e1
CH
120 try
121 {
122 std::ofstream ofs( CacheFile.c_str() );
123 boost::archive::xml_oarchive oa(ofs);
ad83004d
CH
124 oa << boost::serialization::make_nvp("IpCache", IpCache);
125 oa << boost::serialization::make_nvp("CnameCache", CnameCache);
126 GlobalLogger.info() << "DnsCache: saved to cache file " << CacheFile;
946356e1
CH
127
128 HasChanged = false;
129 }
130 catch (std::exception &exc)
131 {
132 GlobalLogger.warning() << "Saving failed: " << exc.what();
133 }
96779587
CH
134}
135
136
137void DnsCache::load_from_cachefile()
138{
946356e1
CH
139 if (CacheFile.empty())
140 {
141 GlobalLogger.warning()
ad83004d 142 << "DnsCache: cannot load because cache file name is empty!";
946356e1
CH
143 return;
144 }
145 else if ( !I2n::file_exists(CacheFile) )
146 {
ad83004d 147 GlobalLogger.warning() << "DnsCache: cannot load because cache file "
946356e1
CH
148 << CacheFile << " does not exist!";
149 return;
150 }
151 try
152 {
153 HostAddressVec cache;
154
155 std::ifstream ifs( CacheFile.c_str() );
156 boost::archive::xml_iarchive ia(ifs);
157
ad83004d
CH
158 ia >> boost::serialization::make_nvp("IpCache", cache);
159 ia >> boost::serialization::make_nvp("CnameCache", cache);
160 GlobalLogger.info() << "DnsCache: loaded from file " << CacheFile;
946356e1
CH
161 }
162 catch (boost::archive::archive_exception &exc)
163 {
ad83004d 164 GlobalLogger.warning() << "DnsCache: archive exception loading from "
946356e1
CH
165 << CacheFile << ": " << exc.what();
166 }
167 catch (std::exception &exc)
168 {
ad83004d 169 GlobalLogger.warning() << "DnsCache: exception while loading from "
946356e1
CH
170 << CacheFile << ": " << exc.what();
171 }
96779587
CH
172}
173
96779587 174void DnsCache::update(const std::string &hostname,
4e7b6ff9 175 const HostAddressVec &new_data)
96779587 176{
ad83004d 177 GlobalLogger.info() << "DnsCache: update IPs for " << hostname
96779587 178 << " to " << new_data.size() << "-list";
ad83004d 179 IpCache[hostname] = new_data;
96779587
CH
180 HasChanged = true;
181}
182
183
ad83004d
CH
184void DnsCache::update(const std::string &hostname,
185 const std::string &cname)
186{
187 GlobalLogger.info() << "DnsCache: update CNAME for " << hostname
188 << " to " << cname;
189 CnameCache[hostname] = cname;
190 HasChanged = true;
191}
192
193
194void DnsCache::update_ttl(const std::string &hostname,
195 const uint32_t new_ttl)
196{
197 GlobalLogger.info() << "DnsCache: ensure TTL for IPs for " << hostname
e18c1337 198 << " is below " << new_ttl << "s";
ad83004d 199 HostAddressVec ips = IpCache[hostname];
ad83004d
CH
200 TimeToLive current_ttl;
201 BOOST_FOREACH( HostAddress &addr, ips )
202 {
203 current_ttl = addr.get_ttl();
204 if (current_ttl.get_value() > new_ttl)
205 {
206 current_ttl.set_value(new_ttl);
207 addr.set_ttl(current_ttl);
208 HasChanged = true;
209 }
210 }
e18c1337 211 IpCache[hostname] = ips;
ad83004d
CH
212}
213
214
215HostAddressVec& DnsCache::get_ips(const std::string &hostname)
96779587 216{
ad83004d
CH
217 GlobalLogger.info() << "DnsCache: request IPs for " << hostname
218 << " --> " << IpCache[hostname].size() << "-list";
219 return IpCache[hostname];
96779587
CH
220}
221
ad83004d
CH
222std::string& DnsCache::get_cname(const std::string &hostname)
223{
224 GlobalLogger.info() << "DnsCache: request CNAME for " << hostname
e18c1337 225 << " --> \"" << CnameCache[hostname] << "\"";
ad83004d
CH
226 return CnameCache[hostname];
227}
228
e18c1337
CH
229HostAddressVec& DnsCache::get_ips_recursive(const std::string &hostname)
230{
231 std::string current_host = hostname;
232 HostAddressVec& result = get_ips(current_host);
233 while ( result.empty() )
234 {
235 current_host = get_cname(current_host);
236 if (current_host.empty())
237 break;
238 else
239 result = get_ips(current_host);
240 }
241 return result;
242}
ad83004d 243
96779587
CH
244// (created using vim -- the world's best text editor)
245