Commit | Line | Data |
---|---|---|
36ad976b 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 | |
4e7b6ff9 CH |
21 | |
22 | with code copied from boost::net::dns::resolve.hpp | |
23 | by Andreas Haberstroh (andreas at ibusy dot com) | |
24 | from https://github.com/softwareace/Boost.DNS | |
36ad976b CH |
25 | */ |
26 | ||
c5b4902d CH |
27 | #include "dns/dnsresolver.h" |
28 | ||
29 | #include <stdint.h> | |
e91538f0 | 30 | #include <sstream> |
36ad976b | 31 | |
4e7b6ff9 CH |
32 | #include <boost/foreach.hpp> |
33 | #include <boost/bind.hpp> | |
34 | #include <boost/function.hpp> | |
35 | #include <boost/net/dns.hpp> | |
36 | #include <boost/date_time/posix_time/posix_time.hpp> | |
ad83004d CH |
37 | #include <boost/uuid/uuid.hpp> |
38 | #include <boost/uuid/uuid_io.hpp> | |
4e7b6ff9 | 39 | |
36ad976b | 40 | #include <logfunc.hpp> |
4e7b6ff9 | 41 | |
36ad976b | 42 | using I2n::Logger::GlobalLogger; |
4e7b6ff9 CH |
43 | using boost::posix_time::seconds; |
44 | using boost::posix_time::minutes; | |
36ad976b CH |
45 | |
46 | namespace Config | |
47 | { | |
96779587 CH |
48 | const int ResolveTimeoutSeconds = 5; |
49 | const int PauseBeforeRetrySeconds = 10; | |
50 | const int StaleDataLongtermMinutes = 15; | |
36ad976b | 51 | const int DNS_PORT = 53; |
36ad976b CH |
52 | } |
53 | ||
4e7b6ff9 CH |
54 | DnsResolver::DnsResolver(IoServiceItem &io_serv, |
55 | const std::string &hostname, | |
923626c0 | 56 | const DnsIpProtocol &protocol, |
96779587 | 57 | const DnsCacheItem cache, |
96779587 | 58 | const boost::asio::ip::address &name_server) |
4e7b6ff9 | 59 | : ResolverBase( io_serv, hostname, cache ) |
e91538f0 CH |
60 | , Socket( *io_serv, ip::udp::endpoint(ip::udp::v4(), 0)) |
61 | // just connect to anything, will specify sender/receiver later | |
4e7b6ff9 | 62 | , ReceiveBuffer() |
ad83004d | 63 | , RequestBuffer() |
923626c0 | 64 | , Protocol( protocol ) |
4e7b6ff9 | 65 | , NameServer( name_server, Config::DNS_PORT ) |
36ad976b CH |
66 | , ResolveTimeoutTimer( *io_serv ) |
67 | , PauseBeforeRetryTimer( *io_serv ) | |
68 | , StaleDataLongtermTimer( *io_serv ) | |
4e7b6ff9 | 69 | , NextIpIndex( 0 ) |
36ad976b CH |
70 | , RetryCount( 0 ) |
71 | , IsResolving( false ) | |
e91538f0 | 72 | , LogPrefix( "DnsResolver" ) |
ad83004d CH |
73 | , RandomIdGenerator() |
74 | , RequestId( 0 ) | |
e91538f0 CH |
75 | { |
76 | std::stringstream temp; | |
77 | temp << "Dns(" << ResolverBase::Hostname << "): "; | |
78 | LogPrefix = temp.str(); | |
ad83004d | 79 | |
e91538f0 | 80 | } |
96779587 | 81 | |
c5b4902d CH |
82 | DnsResolver::~DnsResolver() |
83 | { | |
84 | boost::system::error_code error; | |
e91538f0 | 85 | Socket.shutdown(boost::asio::ip::udp::socket::shutdown_both, error); |
c5b4902d | 86 | if ( error ) |
e91538f0 | 87 | GlobalLogger.warning() << LogPrefix << "Received error " << error |
c5b4902d CH |
88 | << " when shutting down socket for DNS"; |
89 | // in IcmpPinger always gave an error system:9 | |
90 | // (probably EBADF: Bad file descriptor) | |
91 | ||
92 | Socket.close(error); | |
93 | if ( error ) | |
e91538f0 | 94 | GlobalLogger.warning() << LogPrefix << "Received error " << error |
c5b4902d CH |
95 | << " when closing socket for DNS"; |
96 | } | |
97 | ||
98 | ||
96779587 CH |
99 | |
100 | //============================================================================== | |
101 | // ASYNC RESOLVE | |
102 | //============================================================================== | |
36ad976b CH |
103 | |
104 | /** | |
c5b4902d | 105 | * copied here code from boost::net::dns::resolve.hpp, since want async |
36ad976b | 106 | * operation and that is used only internally, there |
36ad976b | 107 | */ |
4e7b6ff9 | 108 | void DnsResolver::do_resolve() |
36ad976b | 109 | { |
36ad976b | 110 | // check if resolving already |
4e7b6ff9 | 111 | if (IsResolving) |
36ad976b | 112 | { |
e91538f0 | 113 | GlobalLogger.info() << LogPrefix |
4e7b6ff9 | 114 | << "Call to do_resolve ignored since resolving already"; |
36ad976b CH |
115 | return; |
116 | } | |
ad83004d | 117 | IsResolving = true; |
36ad976b | 118 | |
e91538f0 CH |
119 | GlobalLogger.info() << LogPrefix << "start resolving"; |
120 | ||
36ad976b CH |
121 | // just to be sure: cancel timers |
122 | ResolveTimeoutTimer.cancel(); | |
123 | PauseBeforeRetryTimer.cancel(); | |
124 | StaleDataLongtermTimer.cancel(); | |
125 | ||
126 | // create DNS request | |
4e7b6ff9 | 127 | boost::net::dns::message dns_message( ResolverBase::Hostname, |
ad83004d | 128 | boost::net::dns::type_a); //all ); DEBUG |
36ad976b | 129 | dns_message.recursive(false); |
4e7b6ff9 CH |
130 | dns_message.action(boost::net::dns::message::query); |
131 | dns_message.opcode(boost::net::dns::message::squery); | |
ad83004d CH |
132 | |
133 | // create random ID for message | |
134 | boost::uuids::uuid message_id = RandomIdGenerator(); | |
135 | memcpy( &RequestId, message_id.data, sizeof(RequestId) ); | |
136 | dns_message.id( RequestId ); | |
137 | GlobalLogger.debug() << LogPrefix << "Request has ID " | |
138 | << std::showbase << std::hex << dns_message.id(); | |
36ad976b CH |
139 | |
140 | // setup receipt of reply | |
141 | Socket.async_receive_from( | |
142 | boost::asio::buffer(ReceiveBuffer.get_array()), | |
143 | NameServer, | |
144 | boost::bind( &DnsResolver::handle_dns_result, this, | |
145 | boost::asio::placeholders::error, | |
146 | boost::asio::placeholders::bytes_transferred) | |
4e7b6ff9 | 147 | ); |
36ad976b CH |
148 | |
149 | // schedule timeout | |
4e7b6ff9 CH |
150 | (void) ResolveTimeoutTimer.expires_from_now( |
151 | seconds(Config::ResolveTimeoutSeconds)); | |
152 | ResolveTimeoutTimer.async_wait( boost::bind( | |
153 | &DnsResolver::handle_resolve_timeout, | |
154 | this, boost::asio::placeholders::error) ); | |
36ad976b CH |
155 | |
156 | // send dns request | |
ad83004d | 157 | dns_message.encode(RequestBuffer); |
e91538f0 CH |
158 | size_t bytes_sent; |
159 | try | |
160 | { | |
161 | bytes_sent = Socket.send_to( | |
ad83004d | 162 | boost::asio::buffer(RequestBuffer.get_array()), |
e91538f0 CH |
163 | NameServer ); |
164 | } | |
165 | catch (boost::system::system_error &err) | |
166 | { | |
167 | GlobalLogger.warning() << LogPrefix | |
168 | << "Sending of DNS request message failed: " | |
169 | << err.what(); | |
170 | handle_unavailable(); | |
171 | return; | |
172 | } | |
173 | ||
174 | if ( bytes_sent == 0 ) | |
175 | { | |
176 | GlobalLogger.warning() << LogPrefix << "Empty DNS request sent!"; | |
177 | handle_unavailable(); | |
178 | return; | |
179 | } | |
180 | ||
181 | GlobalLogger.info() << LogPrefix << "resolving under way"; | |
36ad976b CH |
182 | } |
183 | ||
184 | ||
4e7b6ff9 | 185 | void DnsResolver::handle_dns_result(const boost::system::error_code &error, |
ad83004d | 186 | const std::size_t bytes_transferred) |
36ad976b CH |
187 | { |
188 | if ( error == boost::asio::error::operation_aborted ) // cancelled | |
189 | { | |
e91538f0 CH |
190 | GlobalLogger.info() << LogPrefix |
191 | << "DNS resolve operation was cancelled"; | |
36ad976b | 192 | bool was_success = false; |
96779587 | 193 | finalize_resolve(was_success); |
36ad976b CH |
194 | } |
195 | else if (error) | |
196 | { | |
e91538f0 CH |
197 | GlobalLogger.info() << LogPrefix << "DNS resolve resulted in error " |
198 | << error << " --> treat like unavailable"; | |
36ad976b CH |
199 | handle_unavailable(); |
200 | return; | |
201 | } | |
202 | ||
ad83004d CH |
203 | GlobalLogger.debug() << LogPrefix << "Handling DNS result (" |
204 | << bytes_transferred << " bytes transferred)"; | |
205 | ||
36ad976b CH |
206 | // next 3(+1) lines copied from boost/net/dns/resolver.hpp: |
207 | // clamp the recvBuffer with the number of bytes transferred or decode buffr | |
208 | ReceiveBuffer.length(bytes_transferred); | |
4e7b6ff9 | 209 | boost::net::dns::message result_message; |
36ad976b CH |
210 | result_message.decode( ReceiveBuffer ); |
211 | ||
ad83004d CH |
212 | // check ID |
213 | if (RequestId != result_message.id()) | |
214 | GlobalLogger.warning() << "Received answer for request ID " | |
215 | << std::showbase << std::hex << result_message.id() | |
216 | << " but expected ID " << RequestId; | |
217 | else | |
218 | GlobalLogger.debug() << LogPrefix << "Result has correct ID " | |
219 | << std::showbase << std::hex << RequestId; | |
220 | ||
221 | // loop over answers, remembering ips and cnames | |
4e7b6ff9 CH |
222 | // work with a regular pointer to list of answers since result_message is |
223 | // owner of data and that exists until end of function | |
224 | // Items in answers list are shared_ptr to resource_base_t | |
ad83004d CH |
225 | HostAddressVec ip_list; |
226 | std::vector<std::string> hosts_for_ips; | |
227 | std::vector<string_pair> result_cnames; | |
228 | std::vector<string_pair> result_nameservers; | |
36ad976b | 229 | |
ad83004d CH |
230 | gather_results(result_message.answers(), &ip_list, &hosts_for_ips, |
231 | &result_cnames, &result_nameservers); | |
232 | ||
233 | // remember cname tree (if there were any) | |
234 | // assume each cname points to next ( source --> destination ) | |
235 | std::string source = ResolverBase::Hostname; | |
236 | BOOST_FOREACH( const std::string &cname, result_cnames ) | |
237 | { | |
238 | update_cache( source, cname ); | |
239 | source = cname; | |
240 | } | |
241 | ||
242 | // IPs point to last CNAME (or Hostname if no cnames given) | |
243 | if ( !ip_list.empty() ) | |
244 | { | |
245 | update_cache( source, ip_list ); | |
246 | ||
247 | // clean up | |
248 | bool was_success = true; | |
249 | finalize_resolve(was_success); | |
250 | } | |
251 | else if ( !result_cnames.empty() ) | |
252 | { // no IPs but a cname --> re-start resolving with that | |
253 | handle_cname(source); | |
254 | } | |
255 | else | |
256 | { // no answers --> check for nameservers in authorities section | |
257 | // and corresponding IPs in additional section | |
258 | if ( !result_nameservers.empty() ) | |
259 | GlobalLogger.warning() << "Received NS records in answers! " | |
260 | << "That is quite unexpected..." | |
261 | gather_results(result_message.authorites(), &ip_list, &hosts_for_ips, | |
262 | &result_cnames, &result_nameservers); | |
263 | gather_results(result_message.additionals(), &ip_list, &hosts_for_ips, | |
264 | &result_cnames, &result_nameservers); | |
265 | ||
266 | int index, index_found=-1; | |
267 | // go through name servers | |
268 | BOOST_FOREACH( const string_pair &nameserver, result_nameservers ) | |
269 | { | |
270 | index = 0; | |
271 | // go through ips and look for match | |
272 | BOOST_FOREACH( const std::string &ip_host, hosts_for_ips ) | |
273 | { | |
274 | if (nameserver.second == ip_host) | |
275 | { | |
276 | index_found = index; | |
277 | break; | |
278 | } | |
279 | ++index; | |
280 | } | |
281 | ||
282 | if (index_found > -1) | |
283 | break; | |
284 | } | |
285 | if (index_found > -1) | |
286 | { // have a name server with ip | |
287 | handle_recurse(ip_list[index_found]); | |
288 | ||
289 | ||
290 | GlobalLogger.info() << LogPrefix << "Have " << result_ips.size() | |
291 | << " IPs and " << result_cnames.size() << " CNAMEs"; | |
292 | ||
293 | // We expect either one single CNAME and no IPs or a list of IPs. | |
294 | // But deal with other cases as well | |
295 | if (result_ips.empty() && result_cnames.empty()) | |
296 | handle_unavailable(); // we just got crap, this is a dead end | |
297 | ||
298 | bool do_resolve_cnames = !Config::DnsRequestsAreRecursive; | |
299 | ||
300 | if (Config::DnsRequestsAreRecursive && !result_cnames.empty() | |
301 | && result_ips.empty() ) | |
302 | { | |
303 | GlobalLogger.warning() << LogPrefix << "CNAMES appear to be unresolved" | |
304 | << " although DNS requests are recursive! --> try on our own"; | |
305 | do_resolve_cnames = true; | |
306 | } | |
307 | else | |
308 | GlobalLogger.info() << LogPrefix << "Ignore CNAMES, assume they were " | |
309 | << "resolved"; | |
310 | ||
311 | if (do_resolve_cnames) | |
312 | { | |
313 | BOOST_FOREACH( const std::string &cname, result_cnames ) | |
314 | handle_cname(cname); // will schedule another DNS call | |
315 | } | |
316 | ||
317 | if ( !result_ips.empty() ) | |
318 | handle_ips(result_ips); | |
319 | } | |
320 | ||
321 | void DnsResolver::gather_results(const boost::net::dns::rr_list_t *answers, | |
322 | HostAddressVec *result_ips, | |
323 | std::vector<std::string> *hosts_for_ips, | |
324 | std::vector<string_pair> *result_cnames, | |
325 | std::vector<string_pair> *result_nameservers) | |
326 | const | |
327 | { | |
4e7b6ff9 CH |
328 | using boost::net::dns::resource_base_t; |
329 | BOOST_FOREACH( boost::shared_ptr<resource_base_t> rr_item, *answers ) | |
36ad976b | 330 | { |
4e7b6ff9 | 331 | boost::net::dns::type_t rr_type = rr_item->rtype(); |
ad83004d CH |
332 | uint32_t ttl = rr_item->ttl(); |
333 | std::string domain = rr_item->domain(); | |
36ad976b CH |
334 | |
335 | if (rr_type == boost::net::dns::type_a) | |
336 | { // 'A' resource records carry IPv4 addresses | |
923626c0 CH |
337 | if (Protocol == DNS_IPv6) |
338 | { | |
e91538f0 CH |
339 | GlobalLogger.info() << LogPrefix << "Ignoring IPv4 address " |
340 | << "because resolver was configured to only use IPv6."; | |
923626c0 CH |
341 | continue; |
342 | } | |
4e7b6ff9 CH |
343 | boost::asio::ip::address_v4 ip = |
344 | ( dynamic_cast<boost::net::dns::a_resource *> (rr_item.get()) ) | |
345 | ->address(); | |
ad83004d CH |
346 | hosts_for_ips->push_back( domain ); |
347 | result_ips->push_back( HostAddress(ip, ttl) ); | |
348 | GlobalLogger.debug() << LogPrefix << "IPv4 " << ip << " with TTL " | |
349 | << ttl << "s for " << domain; | |
36ad976b CH |
350 | } |
351 | else if (rr_type == boost::net::dns::type_a6) | |
352 | { // 'AAAA' resource records carry IPv6 addresses | |
923626c0 CH |
353 | if (Protocol == DNS_IPv4) |
354 | { | |
e91538f0 CH |
355 | GlobalLogger.info() << LogPrefix << "Ignoring IPv6 address " |
356 | << "because resolver was configured to only use IPv4."; | |
923626c0 CH |
357 | continue; |
358 | } | |
4e7b6ff9 CH |
359 | boost::asio::ip::address_v6 ip = |
360 | ( dynamic_cast<boost::net::dns::a6_resource *> (rr_item.get()) ) | |
361 | ->address(); | |
ad83004d CH |
362 | hosts_for_ips->push_back( domain ); |
363 | result_ips->push_back( HostAddress(ip, ttl) ); | |
364 | GlobalLogger.debug() << LogPrefix << "IPv6 " << ip << " with TTL " | |
365 | << ttl << "s for " << domain; | |
36ad976b CH |
366 | } |
367 | else if (rr_type == boost::net::dns::type_cname) | |
368 | { // 'CNAME' resource records that carry aliases | |
4e7b6ff9 CH |
369 | std::string cname = |
370 | (dynamic_cast<boost::net::dns::cname_resource *>(rr_item.get())) | |
371 | ->canonicalname(); | |
ad83004d CH |
372 | result_cnames->push_back( string_pair(domain, cname) ); |
373 | GlobalLogger.debug() << LogPrefix << "CNAME " << cname | |
374 | << " with TTL " << ttl << "s for " << domain; | |
36ad976b CH |
375 | } |
376 | else if (rr_type == boost::net::dns::type_ns) | |
ad83004d CH |
377 | { // NS (nameserver) resource records |
378 | std::string nameserver = | |
379 | (dynamic_cast<boost::net::dns::ns_resource *>(rr_item.get())) | |
380 | ->nameserver(); | |
381 | result_nameservers->push_back( string_pair(domain, nameserver) ); | |
382 | GlobalLogger.debug() << LogPrefix << "NameServer " << nameserver | |
383 | << " with TTL " << ttl << "s for " << domain; | |
384 | } | |
36ad976b | 385 | else if (rr_type == boost::net::dns::type_soa) |
e91538f0 | 386 | GlobalLogger.debug() << LogPrefix << "SOA resource"; |
36ad976b | 387 | else if (rr_type == boost::net::dns::type_ptr) |
e91538f0 | 388 | GlobalLogger.debug() << LogPrefix << "ptr resource"; |
36ad976b | 389 | else if (rr_type == boost::net::dns::type_hinfo) |
e91538f0 | 390 | GlobalLogger.debug() << LogPrefix << "hinfo resource"; |
36ad976b | 391 | else if (rr_type == boost::net::dns::type_mx) |
e91538f0 | 392 | GlobalLogger.debug() << LogPrefix << "mx resource"; |
36ad976b | 393 | else if (rr_type == boost::net::dns::type_txt) |
e91538f0 | 394 | GlobalLogger.debug() << LogPrefix << "txt resource"; |
36ad976b | 395 | else if (rr_type == boost::net::dns::type_srv) |
e91538f0 | 396 | GlobalLogger.debug() << LogPrefix << "srv resource"; |
36ad976b | 397 | else if (rr_type == boost::net::dns::type_axfr) |
e91538f0 | 398 | GlobalLogger.debug() << LogPrefix << "axfr resource"; |
36ad976b | 399 | else |
ad83004d CH |
400 | GlobalLogger.debug() << LogPrefix << "unknown resource type: " |
401 | << std::showbase << std::hex | |
402 | << static_cast<unsigned>(rr_item->rtype()); | |
36ad976b | 403 | } |
36ad976b CH |
404 | } |
405 | ||
406 | ||
4e7b6ff9 | 407 | void DnsResolver::handle_ips(const HostAddressVec &ips) |
36ad976b | 408 | { |
36ad976b | 409 | // save in cache |
96779587 | 410 | ResolverBase::update_cache( ips ); |
36ad976b CH |
411 | |
412 | // clean up | |
413 | bool was_success = true; | |
96779587 | 414 | finalize_resolve(was_success); |
36ad976b CH |
415 | } |
416 | ||
417 | ||
418 | void DnsResolver::handle_unavailable() | |
419 | { | |
420 | // schedule new attempt in quite a while | |
4e7b6ff9 CH |
421 | StaleDataLongtermTimer.expires_from_now( |
422 | minutes(Config::StaleDataLongtermMinutes)); | |
36ad976b | 423 | StaleDataLongtermTimer.async_wait( |
4e7b6ff9 CH |
424 | boost::bind( &DnsResolver::wait_timer_timeout_handler, |
425 | this, boost::asio::placeholders::error | |
36ad976b CH |
426 | ) |
427 | ); | |
428 | ||
429 | // for now, admit failure | |
430 | bool was_success = false; | |
96779587 | 431 | finalize_resolve(was_success); |
36ad976b CH |
432 | } |
433 | ||
434 | void DnsResolver::handle_cname(const std::string &canonical_name) | |
435 | { | |
436 | // get resolver for canonical name | |
96779587 | 437 | ResolverItem resolver = DnsMaster::get_instance() |
923626c0 | 438 | ->get_resolver_for(canonical_name, Protocol); |
4e7b6ff9 CH |
439 | callback_type callback = boost::bind( &DnsResolver::cname_resolve_callback, |
440 | this, canonical_name, _1, _2 ); | |
441 | resolver->async_resolve( callback ); | |
36ad976b CH |
442 | |
443 | stop_trying(); | |
444 | } | |
445 | ||
446 | ||
4e7b6ff9 CH |
447 | void DnsResolver::cname_resolve_callback(const std::string &canonical_name, |
448 | const bool was_success, | |
ad83004d | 449 | const int recursion_count) |
36ad976b | 450 | { |
96779587 | 451 | if (was_success) |
4e7b6ff9 | 452 | // tell cache to return cname's ips if queried for our hostname |
96779587 CH |
453 | ResolverBase::update_cache( |
454 | ResolverBase::get_cached_results(canonical_name) ); | |
96779587 | 455 | else |
e91538f0 | 456 | GlobalLogger.info() << LogPrefix << "Cname resolution failed"; |
96779587 | 457 | |
ad83004d CH |
458 | // cname counts like one recursion step more... |
459 | finalize_resolve(was_success, recursion_count+1); | |
460 | } | |
461 | ||
462 | void DnsResolver::handle_recurse(const HostAddress &name_server) | |
463 | { | |
464 | // get resolver for same hostname but using a different name server | |
465 | if (Recursor) | |
466 | { | |
467 | GlobalLogger.warning() << "Recursor has not been reset!"; | |
468 | Recursor.reset(); | |
469 | } | |
470 | ||
471 | Recursor = DnsMaster::get_instance()->get_recursor_for( | |
472 | Hostname, Protocol, name_server.get_ip()); | |
473 | callback_type callback = boost::bind( | |
474 | &DnsResolver::recursive_resolve_callback, | |
475 | this, name_server.get_ttl().get_value(), | |
476 | _1, _2 ); | |
477 | Recursor->async_resolve( callback ); | |
478 | ||
479 | stop_trying(); | |
480 | } | |
481 | ||
482 | ||
483 | ||
484 | void DnsResolver::recursive_resolve_callback(const uint32_t min_ttl, | |
485 | const bool was_success, | |
486 | const int recursion_count) | |
487 | { | |
488 | if (was_success) | |
489 | // make sure the saved TTL is not larger than the one we found here | |
490 | ResolverBase::update_cache_ttl(min_ttl); | |
491 | else | |
492 | GlobalLogger.info() << LogPrefix << "Recursive resolution failed"; | |
493 | ||
494 | // do not need recursor any more; next time re-create from different random | |
495 | // name server | |
496 | if ( !Recursor ) | |
497 | GlobalLogger.warning() << "Recursor was reset before callback!"; | |
498 | else | |
499 | Recursor.reset(); | |
500 | ||
501 | finalize_resolve(was_success, recursion_count+1); | |
502 | ||
96779587 CH |
503 | } |
504 | ||
505 | ||
506 | void DnsResolver::finalize_resolve(const bool was_success, | |
ad83004d | 507 | const int recursion_count) |
96779587 CH |
508 | { |
509 | // stop timers | |
ad83004d | 510 | if (recursion_count > 0) |
96779587 CH |
511 | stop_trying(); |
512 | // else was called already from handle_cname | |
513 | ||
514 | // schedule callbacks, clearing callback list | |
ad83004d | 515 | ResolverBase::schedule_callbacks(was_success, recursion_count); |
96779587 CH |
516 | |
517 | // finalize | |
e91538f0 | 518 | GlobalLogger.notice() << LogPrefix << "Done resolving" |
96779587 | 519 | << " with success = " << was_success |
ad83004d | 520 | << " and recursion_count = " << recursion_count; |
96779587 CH |
521 | IsResolving = false; |
522 | } | |
523 | ||
524 | void DnsResolver::stop_trying() | |
525 | { | |
526 | // cancel timers | |
e91538f0 | 527 | GlobalLogger.debug() << LogPrefix << "Cancelling timers"; |
96779587 CH |
528 | ResolveTimeoutTimer.cancel(); |
529 | PauseBeforeRetryTimer.cancel(); | |
530 | StaleDataLongtermTimer.cancel(); | |
531 | ||
532 | // clean up | |
533 | RetryCount = 0; | |
534 | } | |
535 | ||
4e7b6ff9 | 536 | void DnsResolver::handle_resolve_timeout(const boost::system::error_code &error) |
96779587 CH |
537 | { |
538 | if ( error == boost::asio::error::operation_aborted ) // cancelled | |
539 | { | |
e91538f0 CH |
540 | GlobalLogger.warning() << LogPrefix |
541 | << "Resolve timeout timer was cancelled!"; | |
96779587 CH |
542 | return; |
543 | } | |
544 | else if (error) | |
545 | { | |
e91538f0 CH |
546 | GlobalLogger.warning() << LogPrefix |
547 | << "resolve timeout handler received error " | |
96779587 CH |
548 | << error; |
549 | return; | |
550 | } | |
551 | ||
e91538f0 | 552 | GlobalLogger.notice() << LogPrefix << "DNS resolving timed out"; |
36ad976b | 553 | |
96779587 CH |
554 | // increment timer |
555 | ++RetryCount; | |
556 | ||
923626c0 CH |
557 | if ( RetryCount > DnsMaster::get_instance() |
558 | ->get_max_address_resolution_attempts() ) | |
96779587 CH |
559 | { |
560 | handle_unavailable(); | |
561 | RetryCount = 0; | |
562 | } | |
563 | else | |
564 | { // schedule retry | |
565 | PauseBeforeRetryTimer.expires_from_now( | |
4e7b6ff9 | 566 | seconds(Config::PauseBeforeRetrySeconds)); |
96779587 | 567 | PauseBeforeRetryTimer.async_wait( |
4e7b6ff9 CH |
568 | boost::bind( &DnsResolver::wait_timer_timeout_handler, |
569 | this, boost::asio::placeholders::error) ); | |
96779587 CH |
570 | } |
571 | } | |
572 | ||
573 | void DnsResolver::wait_timer_timeout_handler( | |
574 | const boost::system::error_code &error) | |
575 | { | |
576 | if ( error == boost::asio::error::operation_aborted ) // cancelled | |
e91538f0 CH |
577 | GlobalLogger.warning() << LogPrefix |
578 | << "Resolve timeout timer was cancelled!"; | |
96779587 | 579 | else if (error) |
e91538f0 CH |
580 | GlobalLogger.warning() << LogPrefix |
581 | << "resolve timeout handler received error " | |
96779587 CH |
582 | << error; |
583 | else | |
584 | { | |
e91538f0 | 585 | GlobalLogger.info() << LogPrefix << "Done waiting --> re-try resolve"; |
4e7b6ff9 | 586 | do_resolve(); |
96779587 | 587 | } |
36ad976b CH |
588 | } |
589 | ||
590 | ||
96779587 CH |
591 | //============================================================================== |
592 | // RETRIEVAL | |
593 | //============================================================================== | |
594 | ||
4e7b6ff9 | 595 | HostAddress DnsResolver::get_next_ip() |
36ad976b CH |
596 | { |
597 | // get cached data | |
4e7b6ff9 | 598 | HostAddressVec cached_data = ResolverBase::get_cached_results(); |
96779587 CH |
599 | |
600 | // if no results cached, return default-constructed HostAddress (0.0.0.0) | |
601 | if ( cached_data.empty() ) | |
4e7b6ff9 CH |
602 | { |
603 | HostAddress return_value; | |
604 | return return_value; | |
605 | } | |
36ad976b | 606 | |
96779587 CH |
607 | // check validity of index (cache may have changed since last call) |
608 | if (NextIpIndex >= cached_data.size()) | |
609 | NextIpIndex = 0; | |
36ad976b | 610 | |
96779587 CH |
611 | // return next IP |
612 | return cached_data[NextIpIndex++]; | |
36ad976b CH |
613 | } |
614 | ||
923626c0 CH |
615 | bool DnsResolver::have_up_to_date_ip() |
616 | { | |
617 | // get cached data | |
618 | HostAddressVec cached_data = ResolverBase::get_cached_results(); | |
619 | ||
620 | // get threshold | |
c5b4902d CH |
621 | uint32_t resolved_ip_ttl_threshold = static_cast<uint32_t>( |
622 | DnsMaster::get_instance()->get_resolved_ip_ttl_threshold() ); | |
923626c0 CH |
623 | |
624 | // loop over addresses | |
625 | BOOST_FOREACH( const HostAddress &addr, cached_data ) | |
626 | { | |
627 | uint32_t ttl = addr.get_ttl().get_updated_value(); | |
628 | if ( ttl > resolved_ip_ttl_threshold ) | |
629 | return true; | |
630 | } | |
631 | ||
632 | // if not returned true by now, we have tried all IPs without success | |
633 | return false; | |
634 | } | |
635 | ||
636 | int DnsResolver::get_resolved_ip_count() | |
637 | { | |
638 | return ResolverBase::get_cached_results().size(); | |
639 | } | |
640 | ||
36ad976b CH |
641 | // (created using vim -- the world's best text editor) |
642 |