Branch data Line data Source code
1 : : // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #include <map>
16 : :
17 : : #include <config.h>
18 : :
19 : : #include "zone_entry.h"
20 : : #include "address_request_callback.h"
21 : : #include "nameserver_entry.h"
22 : :
23 : : #include <algorithm>
24 : : #include <boost/foreach.hpp>
25 : : #include <boost/bind.hpp>
26 : : #include <dns/rrttl.h>
27 : : #include <dns/rcode.h>
28 : : #include <dns/rdataclass.h>
29 : :
30 : : using namespace std;
31 : :
32 : : namespace isc {
33 : :
34 : : using namespace isc::dns;
35 : : using namespace isc::util;
36 : : using namespace isc::util::random;
37 : :
38 : : namespace nsas {
39 : :
40 : 92 : ZoneEntry::ZoneEntry(
41 : : isc::resolve::ResolverInterface* resolver,
42 : : const std::string& name, const isc::dns::RRClass& class_code,
43 : : boost::shared_ptr<HashTable<NameserverEntry> > nameserver_table,
44 : : boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru) :
45 : : expiry_(0),
46 : : name_(name), class_code_(class_code), resolver_(resolver),
47 [ + + ][ + - ]: 368 : nameserver_table_(nameserver_table), nameserver_lru_(nameserver_lru)
48 : : {
49 : 92 : in_process_[ANY_OK] = false;
50 : 92 : in_process_[V4_ONLY] = false;
51 : 92 : in_process_[V6_ONLY] = false;
52 [ # # ][ # # ]: 92 : }
53 : :
54 : : namespace {
55 : : // Shorter aliases for frequently used types
56 : : typedef isc::util::locks::scoped_lock<isc::util::locks::recursive_mutex> Lock; // Local lock, nameservers not locked
57 : : typedef boost::shared_ptr<AddressRequestCallback> CallbackPtr;
58 : :
59 : : /*
60 : : * Create a nameserver.
61 : : * Called inside a mutex so it is filled in atomically.
62 : : */
63 : : boost::shared_ptr<NameserverEntry>
64 : 19 : newNs(const std::string* name, const RRClass* class_code) {
65 : : return (boost::shared_ptr<NameserverEntry>(new NameserverEntry(*name,
66 : 38 : *class_code)));
67 : : }
68 : :
69 : : }
70 : :
71 : : /**
72 : : * \short Callback class that ZoneEntry passes to a resolver.
73 : : *
74 : : * We need to ask for the list of nameservers. So we pass ResolverCallback
75 : : * object to it, when it knows the answer, method of this thing will be
76 : : * called.
77 : : *
78 : : * It is a nested friend class and should be considered as a part of ZoneEntry
79 : : * code. It manipulates directly ZoneEntry's data members, locks it and like
80 : : * that. Mostly eliminates C++ bad design of missing lambda functions.
81 : : */
82 : 60 : class ZoneEntry::ResolverCallback :
83 : : public isc::resolve::ResolverInterface::Callback {
84 : : public:
85 : : /// \short Constructor. Pass "this" zone entry
86 : : ResolverCallback(boost::shared_ptr<ZoneEntry> entry) :
87 : 60 : entry_(entry)
88 : : { }
89 : : /**
90 : : * \short It successfully received nameserver list.
91 : : *
92 : : * It fills the nameservers into the ZoneEntry whose callback this is.
93 : : * If there are in the hash table, it is used. If not, they are
94 : : * created. This might still fail, if the list is empty.
95 : : *
96 : : * It then calls process, to go trough the list of nameservers,
97 : : * examining them and seeing if some addresses are already there
98 : : * and to ask for the rest of them.
99 : : */
100 : 26 : virtual void success(MessagePtr response_message) {
101 : 26 : Lock lock(entry_->mutex_);
102 : :
103 : : // TODO: find the correct RRset, not simply the first
104 [ + - + - : 78 : if (!response_message ||
+ + ][ + + ]
105 : 26 : response_message->getRcode() != isc::dns::Rcode::NOERROR() ||
106 : 26 : response_message->getRRCount(isc::dns::Message::SECTION_ANSWER) == 0) {
107 : : // todo: define this
108 : 2 : failureInternal(300);
109 : : }
110 : :
111 : : isc::dns::RRsetIterator rrsi =
112 : 52 : response_message->beginSection(isc::dns::Message::SECTION_ANSWER);
113 [ + - ]: 26 : const isc::dns::RRsetPtr answer = *rrsi;
114 : :
115 [ + - ]: 26 : RdataIteratorPtr iterator(answer->getRdataIterator());
116 : : // If there are no data
117 [ + - ][ + + ]: 26 : if (iterator->isLast()) {
118 [ + - ][ + - ]: 2 : failureInternal(answer->getTTL().getValue());
119 : : return;
120 : : } else {
121 : : /*
122 : : * We store the nameservers we have currently (we might have
123 : : * none, at startup, but when we time out and ask again, we
124 : : * do), so we can just reuse them instead of looking them up in
125 : : * the table or creating them.
126 : : */
127 : : std::map<string, NameserverPtr> old;
128 [ + + ][ + - ]: 28 : BOOST_FOREACH(const NameserverPtr& ptr, entry_->nameservers_) {
[ + - ][ + + ]
[ + + ]
129 [ + - ][ + - ]: 4 : old[ptr->getName()] = ptr;
130 : : }
131 : : /*
132 : : * List of original nameservers we did not ask for IP address
133 : : * yet.
134 : : */
135 [ + - ]: 24 : set<NameserverPtr> old_not_asked;
136 : 24 : old_not_asked.swap(entry_->nameservers_not_asked_);
137 : :
138 : : // Once we have them put aside, remove the original set
139 : : // of nameservers from the entry
140 : 24 : entry_->nameservers_.clear();
141 : : // And put the ones from the answer them, reusing if possible
142 [ + - ][ + - ]: 52 : for (; !iterator->isLast(); iterator->next()) {
[ + + ]
143 : : try {
144 : : // Get the name from there
145 : : Name ns_name(dynamic_cast<const rdata::generic::NS&>(
146 [ + - ][ + - ]: 56 : iterator->getCurrent()).getNSName());
[ + - ][ + - ]
[ # # ]
147 : : // Try to find it in the old ones
148 : : std::map<string, NameserverPtr>::iterator old_ns(old.find(
149 [ + - ]: 56 : ns_name.toText()));
150 : : /*
151 : : * We didn't have this nameserver before. So we just
152 : : * look it up in the hash table or create it.
153 : : */
154 [ + - ]: 28 : if (old_ns == old.end()) {
155 : : // Look it up or create it
156 [ + - ]: 56 : string ns_name_str(ns_name.toText());
157 : : pair<bool, NameserverPtr> from_hash(
158 : 28 : entry_->nameserver_table_->getOrAdd(HashKey(
159 : 28 : ns_name_str, entry_->class_code_), boost::bind(
160 : 28 : newNs, &ns_name_str, &entry_->class_code_)));
161 : : // Make it at the front of the list
162 [ + + ]: 28 : if (from_hash.first) {
163 [ + - ]: 38 : entry_->nameserver_lru_->add(from_hash.second);
164 : : } else {
165 : 9 : entry_->nameserver_lru_->touch(
166 [ + - ]: 9 : from_hash.second);
167 : : }
168 : : // And add it at last to the entry
169 [ + - ]: 28 : entry_->nameservers_.push_back(from_hash.second);
170 : 28 : entry_->nameservers_not_asked_.insert(
171 : 28 : from_hash.second);
172 : : } else {
173 : : // We had it before, reuse it
174 [ # # ]: 0 : entry_->nameservers_.push_back(old_ns->second);
175 : : // Did we ask it already? If not, it is still not
176 : : // asked (the one designing std interface must
177 : : // have been mad)
178 [ # # ]: 0 : if (old_not_asked.find(old_ns->second) !=
179 : 0 : old_not_asked.end())
180 : : {
181 : 0 : entry_->nameservers_not_asked_.insert(
182 : 0 : old_ns->second);
183 : : }
184 : : }
185 : : }
186 : : // OK, we skip this one as it is not NS (log?)
187 : 0 : catch (bad_cast&) { }
188 : : }
189 : :
190 : : // It is unbelievable, but we found no nameservers there
191 [ - + ]: 24 : if (entry_->nameservers_.empty()) {
192 : : // So we fail the same way as if we got empty list
193 [ # # ][ # # ]: 0 : failureInternal(answer->getTTL().getValue());
194 : : return;
195 : : } else {
196 : : // Ok, we have them. So set us as ready, set our
197 : : // expiration time and try to answer what we can, ask
198 : : // if there's still someone to ask.
199 : 24 : entry_->setState(READY);
200 [ + - ]: 24 : entry_->expiry_ = answer->getTTL().getValue() + time(NULL);
201 [ + - ]: 24 : entry_->process(ADDR_REQ_MAX, NameserverPtr());
202 : : return;
203 : : }
204 : : }
205 : : }
206 : : /// \short Failed to receive answer.
207 : 11 : virtual void failure() {
208 : 11 : failureInternal(300);
209 : 11 : }
210 : : private:
211 : : /**
212 : : * \short Common function called when "it did not work"
213 : : *
214 : : * It marks the ZoneEntry as unreachable and processes callbacks (by
215 : : * calling process).
216 : : */
217 : 15 : void failureInternal(time_t ttl) {
218 : 15 : Lock lock(entry_->mutex_);
219 : 15 : entry_->setState(UNREACHABLE);
220 : 15 : entry_->expiry_ = ttl + time(NULL);
221 : : // Process all three callback lists and tell them KO
222 [ + - ]: 15 : entry_->process(ADDR_REQ_MAX, NameserverPtr());
223 : 15 : }
224 : : /// \short The entry we are callback of
225 : : boost::shared_ptr<ZoneEntry> entry_;
226 : : };
227 : :
228 : : void
229 : 402045 : ZoneEntry::addCallback(CallbackPtr callback, AddressFamily family,
230 : : const GlueHints& glue_hints) {
231 : : Lock lock(mutex_);
232 : :
233 : 402045 : bool ask(false);
234 : :
235 : : // Look at expiration time
236 [ + + ][ + + ]: 402045 : if (expiry_ && time(NULL) >= expiry_) {
[ + + ]
237 : 2 : setState(EXPIRED);
238 : : }
239 : :
240 : : // We need to ask (again)
241 [ + + ][ + + ]: 402045 : if (getState() == EXPIRED || getState() == NOT_ASKED) {
[ + + ]
242 : 30 : ask = true;
243 : : }
244 : :
245 : : // We do not have the answer right away, just queue the callback
246 : 402015 : bool execute(!ask && getState() != IN_PROGRESS &&
247 [ + + ][ + - ]: 402045 : callbacks_[family].empty());
[ + + ]
248 : :
249 : : // Unless there was glue
250 [ + + ][ + + ]: 402045 : if (ask && glue_hints.hasGlue(family)) {
[ + + ]
251 [ + - ]: 2 : callback->success(glue_hints.getGlue(family));
252 : : } else {
253 : 402043 : callbacks_[family].push_back(callback);
254 : : }
255 : :
256 [ + + ]: 402045 : if (execute) {
257 : : // Try to process it right away, store if not possible to handle
258 [ + - ]: 402013 : process(family, NameserverPtr());
259 : : return;
260 : : }
261 : :
262 [ + + ]: 32 : if (ask) {
263 : 30 : setState(IN_PROGRESS);
264 : : // Our callback might be directly called from resolve, unlock now
265 : 30 : QuestionPtr question(new Question(Name(name_), class_code_,
266 [ + - ]: 60 : RRType::NS()));
267 : : boost::shared_ptr<ResolverCallback> resolver_callback(
268 [ + - ][ + - ]: 30 : new ResolverCallback(shared_from_this()));
269 [ + - ]: 30 : resolver_->resolve(question, resolver_callback);
270 : : return;
271 : : }
272 : : }
273 : :
274 : : void
275 : 0 : ZoneEntry::removeCallback(const CallbackPtr& callback, AddressFamily family) {
276 : : Lock lock(mutex_);
277 : : std::vector<boost::shared_ptr<AddressRequestCallback> >::iterator i =
278 : 0 : callbacks_[family].begin();
279 [ # # ]: 0 : for (; i != callbacks_[family].end(); ++i) {
280 [ # # ]: 0 : if (*i == callback) {
281 : 0 : callbacks_[family].erase(i);
282 : : // At this point, a callback should only be in the list
283 : : // once (enforced by RunningQuery doing only one at a time)
284 : : // If that changes, we need to revise this (can't delete
285 : : // elements from a list we're looping over)
286 : 0 : return;
287 : : }
288 : : }
289 : : }
290 : :
291 : : namespace {
292 : :
293 : : // This just moves items from one container to another
294 : : template<class Container>
295 : : void
296 : 30 : move(Container& into, Container& from) {
297 : : into.insert(into.end(), from.begin(), from.end());
298 : : from.clear();
299 : 30 : }
300 : :
301 : : // Update the address selector according to the RTTs
302 : : //
303 : : // Each address has a probability to be selected if multiple addresses are available
304 : : // The weight factor is equal to 1/(rtt*rtt), then all the weight factors are normalized
305 : : // to make the sum equal to 1.0
306 : : void
307 : 402027 : updateAddressSelector(std::vector<NameserverAddress>& addresses,
308 : : WeightedRandomIntegerGenerator& selector)
309 : : {
310 : 402027 : vector<double> probabilities;
311 [ + + ][ + - ]: 2810097 : BOOST_FOREACH(NameserverAddress& address, addresses) {
[ + - ][ + + ]
[ + + ]
312 : 1204035 : uint32_t rtt = address.getAddressEntry().getRTT();
313 [ - + ]: 1204035 : if(rtt == 0) {
314 [ # # ][ # # ]: 0 : isc_throw(RTTIsZero, "The RTT is 0");
315 : : }
316 : :
317 [ + + ]: 1204035 : if(rtt == AddressEntry::UNREACHABLE) {
318 [ + - ]: 400000 : probabilities.push_back(0);
319 : : } else {
320 [ + - ]: 1204035 : probabilities.push_back(1.0/(rtt*rtt));
321 : : }
322 : : }
323 : : // Calculate the sum
324 : 402027 : double sum = accumulate(probabilities.begin(), probabilities.end(), 0.0);
325 : :
326 [ + + ]: 402027 : if(sum != 0) {
327 : : // Normalize the probabilities to make the sum equal to 1.0
328 [ + + ]: 1206062 : for(vector<double>::iterator it = probabilities.begin();
329 : 1206062 : it != probabilities.end(); ++it){
330 : 904035 : (*it) /= sum;
331 : : }
332 [ + - ]: 100000 : } else if(!probabilities.empty()){
333 : : // If all the nameservers are unreachable, the sum will be 0
334 : : // So give each server equal opportunity to be selected.
335 [ + + ]: 400000 : for(vector<double>::iterator it = probabilities.begin();
336 : 400000 : it != probabilities.end(); ++it){
337 : 600000 : (*it) = 1.0/probabilities.size();
338 : : }
339 : : }
340 : :
341 : : selector.reset(probabilities);
342 : 402027 : }
343 : :
344 : : }
345 : :
346 : : /**
347 : : * \short Sets given boolean to false when destroyed.
348 : : *
349 : : * This is hack eliminating C++ missing finally. We need to make sure
350 : : * the value gets set to false when we leave the function, so we use
351 : : * a Guard object, that sets it when it gets out of scope.
352 : : */
353 : : class ZoneEntry::ProcessGuard {
354 : : public:
355 : : ProcessGuard(bool& guarded) :
356 : 402084 : guarded_(guarded)
357 : : { }
358 : : ~ ProcessGuard() {
359 : 402220 : guarded_ = false;
360 : : }
361 : : private:
362 : : bool& guarded_;
363 : : };
364 : :
365 : : /**
366 : : * \short Callback from NameserverEntry to us.
367 : : *
368 : : * We registre object of this class whenever some ZoneEntry has a need to be
369 : : * notified of a change (received data) inside its NameserverEntry.
370 : : *
371 : : * This is part of the ZoneEntry code (not visible from outside, accessing
372 : : * private functions). It is here just because C++ does not know propper lambda
373 : : * functions.
374 : : */
375 : 150 : class ZoneEntry::NameserverCallback : public NameserverEntry::Callback {
376 : : public:
377 : : /**
378 : : * \short Constructor.
379 : : *
380 : : * \param entry The ZoneEntry to be notified.
381 : : * \param family For which address family this change is, so we
382 : : * do not process all the nameserves and callbacks there.
383 : : */
384 : : NameserverCallback(boost::shared_ptr<ZoneEntry> entry, AddressFamily family) :
385 : : entry_(entry),
386 : 225 : family_(family)
387 : : { }
388 : : /**
389 : : * \short Callback method.
390 : : *
391 : : * This is called by NameserverEntry when the change happens.
392 : : * We just call process to go trough relevant nameservers and call
393 : : * any callbacks we can.
394 : : */
395 : 75 : virtual void operator()(NameserverPtr ns) {
396 : 75 : entry_->process(family_, ns);
397 : 75 : }
398 : : private:
399 : : boost::shared_ptr<ZoneEntry> entry_;
400 : : AddressFamily family_;
401 : : };
402 : :
403 : : void
404 : 29 : ZoneEntry::dispatchFailures(AddressFamily family) {
405 : : // We extract all the callbacks
406 : 29 : vector<CallbackPtr> callbacks;
407 [ + + ]: 29 : if (family == ADDR_REQ_MAX) {
408 [ + - ]: 15 : move(callbacks_[ANY_OK], callbacks_[V4_ONLY]);
409 [ + - ]: 15 : move(callbacks_[ANY_OK], callbacks_[V6_ONLY]);
410 : : family = ANY_OK;
411 : : }
412 : 29 : callbacks.swap(callbacks_[family]);
413 [ + + ][ + - ]: 57 : BOOST_FOREACH(const CallbackPtr& callback, callbacks) {
[ + - ][ + + ]
[ + + ]
414 [ + - ]: 14 : callback->unreachable();
415 : : }
416 : 29 : }
417 : :
418 : : void
419 : 402220 : ZoneEntry::process(AddressFamily family,
420 : : const boost::shared_ptr<NameserverEntry>& nameserver)
421 : : {
422 : : Lock lock(mutex_);
423 [ + + - ]: 402220 : switch (getState()) {
424 : : // These are not interesting, nothing to return now
425 : : case NOT_ASKED:
426 : : case IN_PROGRESS:
427 : : case EXPIRED:
428 : : break;
429 : : case UNREACHABLE: {
430 : 19 : dispatchFailures(family);
431 : : // And we do nothing more now
432 : 19 : break;
433 : : }
434 : : case READY:
435 [ + + ]: 402201 : if (family == ADDR_REQ_MAX) {
436 : : // Just process each one separately
437 : : // TODO Think this over, is it safe, to unlock in the middle?
438 : 24 : process(ANY_OK, nameserver);
439 : 24 : process(V4_ONLY, nameserver);
440 : 24 : process(V6_ONLY, nameserver);
441 : : } else {
442 : : // Nothing to do anyway for this family, be dormant
443 [ + + ]: 402177 : if (callbacks_[family].empty()) {
444 : : return;
445 : : }
446 : : /*
447 : : * If we have multiple nameservers and more than 1 of them
448 : : * is in the cache, we want to choose from all their addresses.
449 : : * So we ensure this instance of process is the only one on
450 : : * the stack. If not, we terminate and let the outernmost
451 : : * one handle it when we return to it.
452 : : *
453 : : * If we didn't do it, one instance would call "resolve". If it
454 : : * was from cache, it would imediatelly recurse back to another
455 : : * process (trough the nameserver callback, etc), which would
456 : : * take that only one nameserver and trigger all callbacks.
457 : : * Only then would resolve terminate and we could ask for the
458 : : * second nameserver. This way, we first receive all the
459 : : * nameservers that are already in cache and trigger the
460 : : * callbacks only then.
461 : : *
462 : : * However, this does not wait for external fetches of
463 : : * nameserver addresses, as the callback is called after
464 : : * process terminates. Therefore this waits only for filling
465 : : * of the nameservers which we already have in cache.
466 : : */
467 [ + + ]: 402086 : if (in_process_[family]) {
468 : : return;
469 : : }
470 : : // Mark we are on the stack
471 : 402084 : ProcessGuard guard(in_process_[family]);
472 : 402084 : in_process_[family] = true;
473 : : // Variables to store the data to
474 [ + - ][ + - ]: 402084 : NameserverEntry::AddressVector addresses;
475 [ + - ][ + - ]: 402084 : NameserverVector to_ask;
476 : 402084 : bool pending(false);
477 : :
478 : : // Pick info from the nameservers
479 [ + + ][ + - ]: 2006288 : BOOST_FOREACH(const NameserverPtr& ns, nameservers_) {
[ + - ][ + + ]
[ + + ]
480 : : Fetchable::State ns_state(ns->getAddresses(addresses,
481 [ + - ]: 802102 : family, ns == nameserver));
482 [ + + + ]: 802102 : switch (ns_state) {
483 : : case IN_PROGRESS:
484 : 2055 : pending = true;
485 : : // Someone asked it, but not us, we don't have
486 : : // callback
487 [ + + ]: 2055 : if (nameservers_not_asked_.find(ns) !=
488 : 4110 : nameservers_not_asked_.end())
489 : : {
490 [ + - ]: 3 : to_ask.push_back(ns);
491 : : }
492 : : break;
493 : : case NOT_ASKED:
494 : : case EXPIRED:
495 [ + - ]: 802102 : to_ask.push_back(ns);
496 : : break;
497 : : case UNREACHABLE:
498 : : case READY:
499 : : // Not interested, but avoiding warning
500 : : break;
501 : : }
502 : : }
503 : :
504 : : // We have someone to ask, so do it
505 [ + + ]: 402084 : if (!to_ask.empty()) {
506 : : // We ask everything that makes sense now
507 : 21 : nameservers_not_asked_.clear();
508 : : /*
509 : : * TODO: Possible place for an optimisation. We now ask
510 : : * everything we can. We should limit this to something like
511 : : * 2 concurrent NS fetches (and fetch cache first, then
512 : : * fetch the remote ones). But fetching everything right
513 : : * away is simpler.
514 : : */
515 [ + + ][ + - ]: 71 : BOOST_FOREACH(const NameserverPtr& ns, to_ask) {
[ + - ][ + + ]
[ + + ]
516 : : // Put all 3 callbacks there. If we put just the
517 : : // current family, it might not work due to missing
518 : : // callback for different one.
519 : : // If they recurse back to us (call directly), we kill
520 : : // it by the in_process_
521 [ + - ]: 25 : insertCallback(ns, ADDR_REQ_MAX);
522 : : }
523 : : // Retry with all the data that might have arrived
524 : 21 : in_process_[family] = false;
525 : : // We do not provide the callback again
526 [ + - ]: 21 : process(family, nameserver);
527 : : // And be done
528 : : return;
529 : : // We have some addresses to answer
530 [ + + ]: 402063 : } else if (!addresses.empty()) {
531 : : // Prepare the selector of addresses
532 : : // TODO: Think of a way how to keep it for a while
533 : : // (not update every time)
534 [ + - ]: 402027 : updateAddressSelector(addresses, address_selector);
535 : :
536 : : // Extract the callbacks
537 [ + - ]: 402027 : vector<CallbackPtr> to_execute;
538 : : // FIXME: Think of a solution where we do not lose
539 : : // any callbacks upon exception
540 : 402027 : to_execute.swap(callbacks_[family]);
541 : :
542 : : // Run the callbacks
543 [ + + ][ + - ]: 1206085 : BOOST_FOREACH(const CallbackPtr& callback, to_execute) {
[ + - ][ + + ]
[ + + ]
544 [ + - ]: 804058 : callback->success(addresses[address_selector()]);
545 : : }
546 : : return;
547 [ + + ]: 36 : } else if (!pending) {
548 [ + - ]: 10 : dispatchFailures(family);
549 : : return;
550 : : }
551 : : }
552 : : return;
553 : : }
554 : : }
555 : :
556 : : void
557 : 100 : ZoneEntry::insertCallback(NameserverPtr ns, AddressFamily family) {
558 [ + + ]: 100 : if (family == ADDR_REQ_MAX) {
559 [ + - ]: 25 : insertCallback(ns, ANY_OK);
560 [ + - ]: 25 : insertCallback(ns, V4_ONLY);
561 [ + - ]: 25 : insertCallback(ns, V6_ONLY);
562 : : } else {
563 : : boost::shared_ptr<NameserverCallback> callback(new NameserverCallback(
564 [ + - ]: 75 : shared_from_this(), family));
565 [ + - ]: 75 : ns->askIP(resolver_, callback, family);
566 : : }
567 : 100 : }
568 : :
569 : : }; // namespace nsas
570 : 402035 : }; // namespace isc
|