d180a89a72aa45d7646585c01cec7bd96abdf428
[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     : DataCache()
52     , SaveTimer( *io_serv )
53     , CacheFile( cache_file )
54     , HasChanged( false )
55 {
56     // load cache from file
57     load_from_cachefile();
58
59     // schedule next save
60     (void) SaveTimer.expires_from_now( seconds( Config::SaveTimerSeconds ) );
61     SaveTimer.async_wait( bind( &DnsCache::schedule_save, this,
62                                 boost::asio::placeholders::error ) );
63 }
64
65
66 DnsCache::~DnsCache()
67 {
68     GlobalLogger.info() << "Dns Cache is being destructed";
69
70     // save one last time without re-scheduling the next save
71     save_to_cachefile();
72
73     // cancel save timer
74     SaveTimer.cancel();
75 }
76
77
78 void DnsCache::schedule_save(const boost::system::error_code &error)
79 {
80     // just in case: ensure SaveTimer is cancelled
81     SaveTimer.cancel();  // (will do nothing if already expired/cancelled)
82
83     if ( error ==  boost::asio::error::operation_aborted )   // cancelled
84     {
85         GlobalLogger.error() << "DNS Cache: SaveTimer was cancelled "
86                              << "--> no save and no re-schedule of saving!";
87         return;
88     }
89     else if (error)
90     {
91         GlobalLogger.error() << "DNS Cache: Received error " << error
92                              << " in schedule_save "
93                              << "--> no save now but re-schedule saving";
94     }
95     else
96         save_to_cachefile();
97
98     // schedule next save
99     (void) SaveTimer.expires_from_now( seconds( Config::SaveTimerSeconds ) );
100     SaveTimer.async_wait( bind( &DnsCache::schedule_save, this,
101                                 boost::asio::placeholders::error ) );
102 }
103
104 void DnsCache::save_to_cachefile()
105 {
106     if (!HasChanged)
107     {
108         GlobalLogger.info() << "DNS Cache: skip saving because has not changed";
109         return;
110     }
111     else if (CacheFile.empty())
112     {
113         GlobalLogger.warning()
114                            << "DNS Cache: skip saving because file name empty!";
115         return;
116     }
117
118     try
119     {
120         std::ofstream ofs( CacheFile.c_str() );
121         boost::archive::xml_oarchive oa(ofs);
122         oa << boost::serialization::make_nvp("DataCache", DataCache);
123         GlobalLogger.info() << "DNS Cache: saved to cache file " << CacheFile;
124
125         HasChanged = false;
126     }
127     catch (std::exception &exc)
128     {
129         GlobalLogger.warning() << "Saving failed: " << exc.what();
130     }
131 }
132
133
134 void DnsCache::load_from_cachefile()
135 {
136     if (CacheFile.empty())
137     {
138         GlobalLogger.warning()
139                   << "DNS Cache: cannot load because cache file name is empty!";
140         return;
141     }
142     else if ( !I2n::file_exists(CacheFile) )
143     {
144         GlobalLogger.warning() << "DNS Cache: cannot load because cache file "
145                                << CacheFile << " does not exist!";
146         return;
147     }
148     try
149     {
150         HostAddressVec cache;
151
152         std::ifstream ifs( CacheFile.c_str() );
153         boost::archive::xml_iarchive ia(ifs);
154
155         ia >> boost::serialization::make_nvp("DataCache", cache);
156         GlobalLogger.info() << "DNS Cache: loaded from file " << CacheFile;
157     }
158     catch (boost::archive::archive_exception &exc)
159     {
160         GlobalLogger.warning() << "DNS Cache: archive exception loading from "
161                                << CacheFile << ": " << exc.what();
162     }
163     catch (std::exception &exc)
164     {
165         GlobalLogger.warning() << "DNS Cache: exception while loading from "
166                                << CacheFile << ": " << exc.what();
167     }
168 }
169
170 /**
171  * 
172  * in case the cached set is equal to the given new one, the old one is kept
173  *   with TTLs updated
174  * @returns true if changed cache; returns false if new_data is same as cache
175  */
176 void DnsCache::update(const std::string &hostname,
177                       const HostAddressVec &new_data)
178 {
179     GlobalLogger.info() << "DNS Cache: update IPs for " << hostname
180                         << " to " << new_data.size() << "-list";
181     DataCache[hostname] = new_data;
182     HasChanged = true;
183 }
184
185
186 HostAddressVec& DnsCache::get_data(const std::string &hostname)
187 {
188     GlobalLogger.info() << "DNS Cache: request IPs for " << hostname
189                         << " --> " << DataCache[hostname].size() << "-list";
190     return DataCache[hostname];
191 }
192
193 // (created using vim -- the world's best text editor)
194