9e8d1e0d99bd3df757949ac678134f0bdd11670d
[pingcheck] / src / dns / dnscache.cpp
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
23 #include "dns/dnscache.h"
24
25 #include <fstream>
26 #include <logfunc.hpp>
27 #include <filefunc.hxx>   // I2n::file_exists
28 #include <boost/bind.hpp>
29 #include <boost/date_time/posix_time/posix_time.hpp>
30 #include <boost/asio/placeholders.hpp>
31 #include <boost/serialization/serialization.hpp>
32 #include <boost/serialization/map.hpp>
33 #include <boost/serialization/string.hpp>
34 #include <boost/serialization/vector.hpp>
35 #include <boost/archive/xml_oarchive.hpp>
36 #include <boost/archive/xml_iarchive.hpp>
37
38 using boost::bind;
39 using boost::posix_time::seconds;
40 using I2n::Logger::GlobalLogger;
41
42 namespace Config
43 {
44     int SaveTimerSeconds = 60;
45 }
46
47
48
49 DnsCache::DnsCache(const IoServiceItem &io_serv,
50                    const std::string &cache_file)
51     : IpCache()
52     , CnameCache()
53     , SaveTimer( *io_serv )
54     , CacheFile( cache_file )
55     , HasChanged( false )
56 {
57     // load cache from file
58     load_from_cachefile();
59
60     // schedule next save
61     (void) SaveTimer.expires_from_now( seconds( Config::SaveTimerSeconds ) );
62     SaveTimer.async_wait( bind( &DnsCache::schedule_save, this,
63                                 boost::asio::placeholders::error ) );
64 }
65
66
67 DnsCache::~DnsCache()
68 {
69     GlobalLogger.info() << "Dns Cache is being destructed";
70
71     // save one last time without re-scheduling the next save
72     save_to_cachefile();
73
74     // cancel save timer
75     SaveTimer.cancel();
76 }
77
78
79 void DnsCache::schedule_save(const boost::system::error_code &error)
80 {
81     // just in case: ensure SaveTimer is cancelled
82     SaveTimer.cancel();  // (will do nothing if already expired/cancelled)
83
84     if ( error ==  boost::asio::error::operation_aborted )   // cancelled
85     {
86         GlobalLogger.error() << "DnsCache: SaveTimer was cancelled "
87                              << "--> no save and no re-schedule of saving!";
88         return;
89     }
90     else if (error)
91     {
92         GlobalLogger.error() << "DnsCache: Received error " << error
93                              << " in schedule_save "
94                              << "--> no save now but re-schedule saving";
95     }
96     else
97         save_to_cachefile();
98
99     // schedule next save
100     (void) SaveTimer.expires_from_now( seconds( Config::SaveTimerSeconds ) );
101     SaveTimer.async_wait( bind( &DnsCache::schedule_save, this,
102                                 boost::asio::placeholders::error ) );
103 }
104
105 void DnsCache::save_to_cachefile()
106 {
107     if (!HasChanged)
108     {
109         GlobalLogger.info() << "DnsCache: skip saving because has not changed";
110         return;
111     }
112     else if (CacheFile.empty())
113     {
114         GlobalLogger.warning()
115                            << "DnsCache: skip saving because file name empty!";
116         return;
117     }
118
119     try
120     {
121         std::ofstream ofs( CacheFile.c_str() );
122         boost::archive::xml_oarchive oa(ofs);
123         oa << boost::serialization::make_nvp("IpCache", IpCache);
124         oa << boost::serialization::make_nvp("CnameCache", CnameCache);
125         GlobalLogger.info() << "DnsCache: saved to cache file " << CacheFile;
126
127         HasChanged = false;
128     }
129     catch (std::exception &exc)
130     {
131         GlobalLogger.warning() << "Saving failed: " << exc.what();
132     }
133 }
134
135
136 void DnsCache::load_from_cachefile()
137 {
138     if (CacheFile.empty())
139     {
140         GlobalLogger.warning()
141                   << "DnsCache: cannot load because cache file name is empty!";
142         return;
143     }
144     else if ( !I2n::file_exists(CacheFile) )
145     {
146         GlobalLogger.warning() << "DnsCache: cannot load because cache file "
147                                << CacheFile << " does not exist!";
148         return;
149     }
150     try
151     {
152         HostAddressVec cache;
153
154         std::ifstream ifs( CacheFile.c_str() );
155         boost::archive::xml_iarchive ia(ifs);
156
157         ia >> boost::serialization::make_nvp("IpCache", cache);
158         ia >> boost::serialization::make_nvp("CnameCache", cache);
159         GlobalLogger.info() << "DnsCache: loaded from file " << CacheFile;
160     }
161     catch (boost::archive::archive_exception &exc)
162     {
163         GlobalLogger.warning() << "DnsCache: archive exception loading from "
164                                << CacheFile << ": " << exc.what();
165     }
166     catch (std::exception &exc)
167     {
168         GlobalLogger.warning() << "DnsCache: exception while loading from "
169                                << CacheFile << ": " << exc.what();
170     }
171 }
172
173 void DnsCache::update(const std::string &hostname,
174                       const HostAddressVec &new_data)
175 {
176     GlobalLogger.info() << "DnsCache: update IPs for " << hostname
177                         << " to " << new_data.size() << "-list";
178     IpCache[hostname] = new_data;
179     HasChanged = true;
180 }
181
182
183 void DnsCache::update(const std::string &hostname,
184                       const std::string &cname)
185 {
186     GlobalLogger.info() << "DnsCache: update CNAME for " << hostname
187                         << " to " << cname;
188     CnameCache[hostname] = cname;
189     HasChanged = true;
190 }
191
192
193 void DnsCache::update_ttl(const std::string &hostname,
194                           const uint32_t new_ttl)
195 {
196     GlobalLogger.info() << "DnsCache: ensure TTL for IPs for " << hostname
197                         << " is below " << new_ttl;
198     HostAddressVec ips = IpCache[hostname];
199     uint32_t current_ttl;
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     }
211     IpCache[hostname] = ip;
212 }
213
214
215 HostAddressVec& DnsCache::get_ips(const std::string &hostname)
216 {
217     GlobalLogger.info() << "DnsCache: request IPs for " << hostname
218                         << " --> " << IpCache[hostname].size() << "-list";
219     return IpCache[hostname];
220 }
221
222 std::string& DnsCache::get_cname(const std::string &hostname)
223 {
224     GlobalLogger.info() << "DnsCache: request CNAME for " << hostname
225                         << " --> " << CnameCache[hostname];
226     return CnameCache[hostname];
227 }
228
229
230
231 // (created using vim -- the world's best text editor)
232