changed how dns deals with cnames and recursion: remember cnames and implement recurs...
[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
96779587
CH
28#include <boost/bind.hpp>
29#include <boost/date_time/posix_time/posix_time.hpp>
30#include <boost/asio/placeholders.hpp>
946356e1
CH
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>
96779587
CH
37
38using boost::bind;
39using boost::posix_time::seconds;
40using I2n::Logger::GlobalLogger;
41
42namespace Config
43{
44 int SaveTimerSeconds = 60;
45}
46
47
48
49DnsCache::DnsCache(const IoServiceItem &io_serv,
50 const std::string &cache_file)
ad83004d
CH
51 : IpCache()
52 , CnameCache()
96779587
CH
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
67DnsCache::~DnsCache()
68{
e91538f0
CH
69 GlobalLogger.info() << "Dns Cache is being destructed";
70
96779587
CH
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
79void 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 {
ad83004d 86 GlobalLogger.error() << "DnsCache: SaveTimer was cancelled "
96779587
CH
87 << "--> no save and no re-schedule of saving!";
88 return;
89 }
90 else if (error)
91 {
ad83004d 92 GlobalLogger.error() << "DnsCache: Received error " << error
96779587
CH
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
105void DnsCache::save_to_cachefile()
106{
107 if (!HasChanged)
108 {
ad83004d 109 GlobalLogger.info() << "DnsCache: skip saving because has not changed";
96779587
CH
110 return;
111 }
946356e1
CH
112 else if (CacheFile.empty())
113 {
114 GlobalLogger.warning()
ad83004d 115 << "DnsCache: skip saving because file name empty!";
946356e1
CH
116 return;
117 }
96779587 118
946356e1
CH
119 try
120 {
121 std::ofstream ofs( CacheFile.c_str() );
122 boost::archive::xml_oarchive oa(ofs);
ad83004d
CH
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;
946356e1
CH
126
127 HasChanged = false;
128 }
129 catch (std::exception &exc)
130 {
131 GlobalLogger.warning() << "Saving failed: " << exc.what();
132 }
96779587
CH
133}
134
135
136void DnsCache::load_from_cachefile()
137{
946356e1
CH
138 if (CacheFile.empty())
139 {
140 GlobalLogger.warning()
ad83004d 141 << "DnsCache: cannot load because cache file name is empty!";
946356e1
CH
142 return;
143 }
144 else if ( !I2n::file_exists(CacheFile) )
145 {
ad83004d 146 GlobalLogger.warning() << "DnsCache: cannot load because cache file "
946356e1
CH
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
ad83004d
CH
157 ia >> boost::serialization::make_nvp("IpCache", cache);
158 ia >> boost::serialization::make_nvp("CnameCache", cache);
159 GlobalLogger.info() << "DnsCache: loaded from file " << CacheFile;
946356e1
CH
160 }
161 catch (boost::archive::archive_exception &exc)
162 {
ad83004d 163 GlobalLogger.warning() << "DnsCache: archive exception loading from "
946356e1
CH
164 << CacheFile << ": " << exc.what();
165 }
166 catch (std::exception &exc)
167 {
ad83004d 168 GlobalLogger.warning() << "DnsCache: exception while loading from "
946356e1
CH
169 << CacheFile << ": " << exc.what();
170 }
96779587
CH
171}
172
96779587 173void DnsCache::update(const std::string &hostname,
4e7b6ff9 174 const HostAddressVec &new_data)
96779587 175{
ad83004d 176 GlobalLogger.info() << "DnsCache: update IPs for " << hostname
96779587 177 << " to " << new_data.size() << "-list";
ad83004d 178 IpCache[hostname] = new_data;
96779587
CH
179 HasChanged = true;
180}
181
182
ad83004d
CH
183void 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
193void 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
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
225 << " --> " << CnameCache[hostname];
226 return CnameCache[hostname];
227}
228
229
230
96779587
CH
231// (created using vim -- the world's best text editor)
232