Commit | Line | Data |
---|---|---|
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 | |
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) | |
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 | ||
67 | DnsCache::~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 | ||
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 | { | |
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 | ||
105 | void 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 | ||
136 | void 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 | 173 | void 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 |
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) | |
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 |
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 | ||
96779587 CH |
231 | // (created using vim -- the world's best text editor) |
232 |